Недавно наткнулся на занимательный merge request по замене зависимости isNumber. Удивительно было в целом осознавать, что как такого универсального метода по определению числа в переменной нет в базовой концепции JavaScript. И данная проблема породила npm-репозиторий isNumber c почти 72 миллионами еженедельных скачиваний на сентябрь 2024 года. Но стоит ли в очередной раз использовать мизерную зависимость в своём проекте? Предлагаю взглянуть на решение, представленное в ранее сказанном mr-е.
Разбор решения
Концепция isNumber проста: функция, в аргументе которого должна быть определяемая переменная, должна вывести нам true или false; true если да, false если нет. Всё просто!
Вот так выглядит данная функция в реализации:
const isNumber = (v) => (typeof v === "number" && v - v === 0) || (typeof v === "string" && Number.isFinite(+v) && v.trim() !== "");
Выглядит тяжко, не так ли? Давайте разберём каждый участок кода и выясним, решает ли эта функция нашу задачу.
Функция принимает аргумент v(variable), с которым и предстоит работать.
Суть работы заключается по-большей части в том, чтобы отловить любую выдачу типа number в данной переменной.
Первая часть проверяет напрямую числовое значение(даже тот же Infinite)
С
typeof v === "number"всё вполне ясно. Прямая проверка типаv - v === 0будет возвращать true в случае с числом, так как если бы мы работали, например, со строкой, то такое выражение возвращало быNaN. Но в случае с массивами это бы не прокатило и данное выражение выдало бы нам тоже 0. Двигаемся дальше.
Вторая часть проверяет строки, которые могут быть преобразованы в числа.
typeof v === "string"проверяет, является ли переменнаяvстрокой.Number.isFinite(+v)— здесь строкаvприводится к числу с помощью унарного оператора+. Если после преобразования строка является конечным числом (то есть неNaN, неInfinite), тоNumber.isFiniteвернётtrue. Это важно для того, чтобы отсеивать строки, которые не могут быть корректно преобразованы в числа (например,"abc"или пустые строки).
Унарный, то есть применённый к одному значению,
+ничего не делает с числовыми значениями. Но если переменная не число, унарный плюс преобразует его в число.
v.trim() !== ""— эта проверка удаляет пробелы в начале и в конце строки и проверяет, что строка не пуста. Это нужно для того, чтобы отсеивать строки, состоящие только из пробелов, которые могут быть преобразованы в0, но на практике не являются корректными представлениями чисел.
Общий взгляд
Подытоживая мы видим две части, которые выполняют следующие задачи:
Проверка чисел (
typeof v === "number" && v - v === 0): Важно не только проверять тип, но и исключатьNaN, который имеет тип"number", но фактически не является числом. Проверка черезv - v === 0— это эффективный способ исключитьNaN, так как для всех других чисел разница числа с самим собой всегда равна нулю.Проверка строк (
typeof v === "string" && Number.isFinite(+v) && v.trim() !== ""): Строки, которые можно преобразовать в числа, часто встречаются в веб-разработке, особенно при работе с формами или API, где данные передаются в виде строк. Важно правильно обработать строки, которые могут быть преобразованы в числа, и отсеять пустые строки или строки, состоящие только из пробелов. ИспользованиеNumber.isFiniteпозволяет проверить, является ли преобразованное значение конечным числом.
Так что же там с репозиторием?
Пакет to-regex-range, речь о котором шла в начале статьи имеет на момент создания merge request-а 43 миллиона скачиваний, а изначальный размер пакета составляет 33Kb. Важно учесть, что основная логика данного пакета состоит из одного среднего по размерам .js файла с парой зависимостей.
После замены пакета isNumber на собственное решение из зависимостей обнаружили, что to-regex-range стал легче на 10Kb. Но эта цифра кажется не играющей, но факт остаётся фактом, что по-итогу небольшое изменение привело к сокращению еженедельного трафика скачивания на 440 гигабайт, сократив с 1.5ТB до 1.0TB. Это уже звучит действительно внушительно.
Package size report
===================
Package info for "to-regex-range@5.0.1": 33 kB
Released: 2019-04-07 06:04:37.03 +0000 UTC (277w2d ago)
Downloads last week: 43,837,006
Estimated traffic last week: 1.5 TB
Removed dependencies:
- is-number@7.0.0: 10 kB (30.06%)
Downloads last week: 43,875,245
Downloads last week from "to-regex-range@5.0.1": 43,837,006 (99.91%)
Estimated traffic last week: 440 GB
Estimated traffic from "to-regex-range@5.0.1": 440 GB (99.91%)
Estimated package size: 33 kB → 23 kB (69.94%)
Estimated traffic over a week: 1.5 TB → 1.0 TB (440 GB saved)
Комментарии (41)

Andchir
17.09.2024 20:15+7После замены пакета
isNumberна собственное решениеНа самом деле там нет собственного решения. Просто взяли код функции из пакета и сделали из него однострочник.

ganqqwerty
17.09.2024 20:15+7Чего-то я не понял. Чей траффик уменьшился? Как инлайн функции связан с траффиком?

Roman2dot0
17.09.2024 20:15+1Связан так, что некоторые выкачивают десятки гигабайт заисимостей ежедневно при сборке, т.к не ставят кэш сервер.

Andchir
17.09.2024 20:15Тут нужно уточнить, что кэширующий сервер нужен только для очень большой команды разработчиков. Для одного разработчика он не нужен (у npm есть свой кэш).

VADemon
17.09.2024 20:15В стародавние времена люди подключали вундер-проги сжатия HTTP-трафика, потому что протокол позволял. Прокси сами, на своих сетях устанавливали провайдеры, кэшируя запросы в пределах своих сетей. Не без причины провайдеры-же организовывали сервисы в интранете, экономя трафик и создавая внутригородской движ. Разумеется, популярностью пользовался Bittorrent внутри ISP.
Это потом интернет стал быстрее и дешевле, государства наглее, а правоторговцы ярее. И лавочка прикрылась, а HTTP покрылся TLS.
Я бы не отказался от кэша Steam (программа есть), дистрибутивов Linux (по HTTP) и других. Да, не всем поможет (у меня последняя миля - VDSL). Банальнейший пример: обновлять три системы на дистрибутиве с rolling release. Большая часть времени уходит на скачивание. Так что может и сам себе прокси поставлю.

VADemon
17.09.2024 20:15Проверка через v - v === 0 — это эффективный способ исключить NaN, так как для всех других чисел разница числа с самим собой всегда равна нулю.
Нет, здесь также бесконечности отсекаются (зачем-то).
Rorg
Где именно в начале статьи идет речь о пакете
to-regex-range? (Если конечно не переходить по ссылке на оригинальный mr)