Skip to main content

Що таке nan у JavaScript?

NaN - це спеціальне числове значення в JavaScript, яке сигналізує про недійсний результат математичної операції, наприклад ділення нуля на нуль або парсинг нечислового рядка.

Теорія

TL;DR

  • NaN означає "Not-a-Number," але його тип все одно "number" (typeof NaN === "number")
  • Уяви отруєну пігулку: один NaN у розрахунку заражає всі наступні результати
  • NaN !== NaN завжди false. Не баг, це специфікація
  • Number.isNaN() - правильний спосіб перевірки. Глобальний isNaN() спочатку перетворює рядки і дає хибні результати

Швидкий приклад

javascript
console.log(0 / 0); // NaN console.log(typeof NaN); // "number" console.log(NaN === NaN); // false console.log(Number.isNaN(NaN)); // true const result = "abc" * 5; console.log(result + 10); // NaN (розповсюджується)

NaN заражає. Будь-яка арифметика з NaN повертає NaN. Один поганий вхід може зруйнувати цілий ланцюг обчислень без жодної помилки.

Чому NaN !== NaN

Стандарт IEEE 754 визначає NaN як "невпорядковане" значення. Два недійсних результати можуть походити від абсолютно різних операцій: 0/0 і Math.sqrt(-1) - обидва NaN, але це різні типи недійсності. Тому стандарт каже: рівність з NaN завжди повертає false. Це змушує явно виявляти його, а не випадково довіряти.

V8 зберігає NaN як бітовий патерн 0x7FF8000000000000. Оператор === порівнює бітові патерни, і будь-яке порівняння з NaN за специфікацією повертає false. Без винятків.

Коли використовувати Number.isNaN()

  • Парсинг введення: перевіряй Number.isNaN() перед будь-яким розрахунком з даними від користувача
  • API відповіді: заміни NaN на null перед відправкою JSON (JSON.stringify все одно конвертує NaN у null, що може непомітно зламати математику на клієнті)
  • Агрегація масивів: відфільтруй NaN перед викликом .reduce()
  • Де б ти не використовував глобальний isNaN(): заміни його на Number.isNaN()

Як JavaScript створює NaN

Недійсні числові операції породжують NaN: ділення нуля на нуль, квадратний корінь з від'ємного числа, парсинг нечислового рядка через Number() або parseInt(). Після цього будь-яка арифметика з NaN автоматично повертає NaN. Зараження відбувається само по собі.

Поширені помилки

Порівняння через ===

javascript
if (result === NaN) { /* ніколи не виконається */ }

NaN не дорівнює нічому, навіть собі. Рішення: Number.isNaN(result).

Використання глобального isNaN() на рядках

javascript
isNaN("hello"); // true (перетворено в NaN) isNaN("123abc"); // true (перетворено в NaN) isNaN("123"); // false (перетворено в 123)

Глобальний isNaN() спочатку конвертує аргумент. Він не може відрізнити "це насправді NaN" від "цей рядок просто не парситься як число." Використовуй Number.isNaN(Number(value)), коли потрібні обидві перевірки.

NaN в JSON

javascript
JSON.stringify({ score: NaN }); // {"score":null}

У JSON немає NaN. Серіалізація конвертує його у null. Якщо клієнт прочитає цей null і спробує робити математику, результати будуть неправильними без жодної помилки. Очищуй дані перед stringify.

NaN отруює Math.min / Math.max

javascript
Math.min(1, NaN, 3); // NaN

Один NaN отруює весь агрегат. Спочатку відфільтруй масив.

Де зустрічається в реальних проєктах

  • React / Formik: Number.isNaN() у валідаторах числових полів перед відправкою форми
  • Express: парсинг req.query.age через parseInt(), перевірка NaN перед запитами до бази
  • D3.js: фільтрація NaN з масивів даних графіків перед обчисленням шкал
  • Lodash: _.isNaN() як зручна обгортка в утилітах

Питання на співбесіді

Q: Чому typeof NaN === "number", якщо NaN не є дійсним числом?
A: Система типів JavaScript слідує IEEE 754. NaN - це частина простору чисел з плаваючою точкою, просто недійсний стан усередині нього. Позначка типу технічно правильна.

Q: Яка різниця між isNaN() і Number.isNaN()?
A: Глобальний isNaN() перетворює аргумент, тому isNaN("foo") повертає true. Number.isNaN() пропускає перетворення і повертає true тільки для самого NaN, тому Number.isNaN("foo") повертає false.

Q: Як обробити NaN в array reduce?
A: Фільтруй перед reduce: .filter(v => !Number.isNaN(v)). Або захисти callback з fallback значенням.

Q: Що робить JSON.stringify з NaN?
A: Конвертує NaN у null. У JSON немає представлення NaN. Завжди очищуй дані перед серіалізацією.

Q: (Senior) V8 зберігає NaN як 0x7FF8000000000000. Чому +NaN === NaN все одно повертає false?
A: Унарний + повертає той самий бітовий патерн. Але IEEE 754 вказує, що будь-яке порівняння з NaN повинно повертати false незалежно від цього. Це навмисно, щоб недійсні значення не могли випадково пройти перевірки рівності.

Приклади

Парсинг введення користувача

javascript
const userAge = "25a"; const age = parseInt(userAge, 10); // NaN if (Number.isNaN(age)) { console.log("Невалідний вік"); // виконується } else { console.log(`Вік через 5 років: ${age + 5}`); } // Output: "Невалідний вік"

parseInt зупиняється на першому нечисловому символі і повертає NaN. Перевіряй до розрахунків, а не після.

Виправлення NaN в array reduce

javascript
const prices = ["$10", "20", "abc"]; // Без виправлення: NaN отруює суму const broken = prices.reduce((sum, p) => sum + Number(p), 0); console.log(broken); // NaN // З виправленням: спочатку фільтруй недійсні значення const total = prices .map(p => Number(p.replace("$", ""))) .filter(v => !Number.isNaN(v)) .reduce((sum, v) => sum + v, 0); console.log(total); // 30

Саме такий патерн ламав продакшен дашборди, де одне погане значення з API перетворювало цілий блок статистики на NaN. Виправлення займає один рядок.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?