Pull to refresh

Как удаление isNumber из зависимостей сэкономил 440 Гб еженедельного трафика

Level of difficultyEasy
Reading time3 min
Views20K

Недавно наткнулся на занимательный 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)

  1. С typeof v === "number" всё вполне ясно. Прямая проверка типа

  2. v - v === 0 будет возвращать true в случае с числом, так как если бы мы работали, например, со строкой, то такое выражение возвращало бы NaN. Но в случае с массивами это бы не прокатило и данное выражение выдало бы нам тоже 0. Двигаемся дальше.

Вторая часть проверяет строки, которые могут быть преобразованы в числа.

  1. typeof v === "string" проверяет, является ли переменная v строкой.

  2. Number.isFinite(+v) — здесь строка v приводится к числу с помощью унарного оператора +. Если после преобразования строка является конечным числом (то есть не NaN, не Infinite), то Number.isFinite вернёт true. Это важно для того, чтобы отсеивать строки, которые не могут быть корректно преобразованы в числа (например, "abc" или пустые строки).

Унарный, то есть применённый к одному значению, + ничего не делает с числовыми значениями. Но если переменная не число, унарный плюс преобразует его в число.

  1. v.trim() !== "" — эта проверка удаляет пробелы в начале и в конце строки и проверяет, что строка не пуста. Это нужно для того, чтобы отсеивать строки, состоящие только из пробелов, которые могут быть преобразованы в 0, но на практике не являются корректными представлениями чисел.

Общий взгляд

Подытоживая мы видим две части, которые выполняют следующие задачи:

  1. Проверка чисел (typeof v === "number" && v - v === 0): Важно не только проверять тип, но и исключать NaN, который имеет тип "number", но фактически не является числом. Проверка через v - v === 0 — это эффективный способ исключить NaN, так как для всех других чисел разница числа с самим собой всегда равна нулю.

  2. Проверка строк (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)

Tags:
Hubs:
Total votes 20: ↑13 and ↓7+8
Comments44

Articles