Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

JavaScript: типи й приведення

20 питань · ~20 хв · Версія v3.0

Увійдіть, щоб продовжити

Примітиви й об'єкти, typeof, неявне приведення й порівняння, NaN, точність чисел і BigInt, оператори ?? і ?. - питання всіх рівнів, від junior до senior.

За спробу
20
У пулі
50
Проходжень
0
Середній бал
-
Пройшли на 70%+
-

Питання для підготовки

14 питань
  • === (строга рівність) - true, лише якщо однакові і тип, і значення. Без перетворень.
  • == (нестрога) спершу приводить операнди до спільного типу за досить заплутаними правилами.
1 === '1';        // false
1 == '1';         // true
0 == '';          // true
0 == '0';         // true
'' == '0';        // false - рівність не транзитивна
null == undefined; // true
null == 0;        // false
[] == false;      // true
NaN === NaN;      // false

Правило: завжди === і !==. Більшість лінтерів (правило eqeqeq) так і вимагає.

Єдиний поширений виняток: value == null - коротка перевірка «null або undefined» одночасно. Але й тут читабельніше value === null || value === undefined або оператор ??.

Ще нюанси:

  • NaN не дорівнює нічому, навіть собі. Перевірка - Number.isNaN(x).
  • Об'єкти й масиви порівнюються за посиланням: [1] === [1] - false, бо це два різні масиви.
  • Object.is(a, b) - як ===, але Object.is(NaN, NaN) дає true, а Object.is(0, -0) - false.

Докладніше в документації: Порівняння рівності

  • undefined - «значення немає, бо його не задали»: неініціалізована змінна, відсутня властивість, параметр без аргументу, результат функції без return.
  • null - «значення свідомо немає»: його присвоюють явно.
let a;              // undefined
const user = {};
user.phone;         // undefined
const selected = null; // свідомо «нічого не вибрано»
typeof undefined;   // 'undefined'
typeof null;        // 'object' - історична помилка мови

|| проти ??:

  • a || b повертає b, якщо a будь-яке хибне значення: 0, '', false, NaN, null, undefined.
  • a ?? b повертає b, лише якщо a - null або undefined.
const page = 0;
page || 1;   // 1 - помилка: нуль - валідне значення
page ?? 1;   // 0

const title = '';
title || 'Без назви';  // 'Без назви'
title ?? 'Без назви';  // ''

Правило: для значень за замовчуванням - ??, якщо 0, '' чи false мають бути збережені.

Поруч живе ?. (optional chaining): user?.address?.city повертає undefined, якщо будь-яка ланка null чи undefined, замість TypeError. І присвоєння ??=: options.timeout ??= 5000.

Докладніше в документації: Оператор ??

Усі числа в JavaScript (крім BigInt) - 64-бітні числа з плаваючою комою за стандартом IEEE 754. Більшість десяткових дробів, як 0.1, у двійковій системі - нескінченні дроби, тож зберігаються з крихітною похибкою.

0.1 + 0.2;           // 0.30000000000000004
0.1 + 0.2 === 0.3;   // false
1.005 * 100;         // 100.49999999999999

Це не баг JavaScript - так само поводяться float у PHP, Python, Java.

Як порівнювати дроби: з допуском:

Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON; // true

Як рахувати гроші:

  • У найменших одиницях - копійках, як цілі числа: 1999 замість 19.99. Цілі числа до Number.MAX_SAFE_INTEGER (≈ 9 квадрильйонів) представлені точно.
  • Округлення - явне й в одному місці: Math.round(price * quantity).
  • Для складних розрахунків (відсотки, курси, розподіл) - бібліотеки десяткової арифметики (decimal.js, big.js, dinero.js).
  • Показ користувачу - через Intl.NumberFormat:
new Intl.NumberFormat('uk-UA', { style: 'currency', currency: 'UAH' }).format(1999 / 100);
// '19,99 ₴'

toFixed() - для показу, не для розрахунків: він повертає рядок і сам округлює з тією ж похибкою ((1.005).toFixed(2) дає '1.00').

Докладніше в документації: Number

У логічному контексті (if, &&, ||, !, тернарний оператор) кожне значення перетворюється на true чи false. Хибних значень небагато:

false, 0, -0, 0n, '' (порожній рядок), null, undefined, NaN.

Усе інше - істинне, і саме тут сюрпризи:

Boolean('0');      // true - непорожній рядок
Boolean('false');  // true
Boolean([]);       // true - порожній масив
Boolean({});       // true - порожній об'єкт

Де це підводить:

  • Нуль як валідне значення:
function setVolume(level) {
  if (!level) level = 50;   // гучність 0 перетвориться на 50
}

Правильно: level ??= 50 чи явна перевірка level === undefined.

  • Перевірка порожнього масиву: if (items) завжди істинне. Потрібно if (items.length > 0).
  • Рендер у React: {count && <Badge />} при count === 0 виведе на сторінку 0. Правильно: {count > 0 && <Badge />}.
  • Рядки з форми: значення з <input> завжди рядок, і '0' - істинний.

!!value чи Boolean(value) явно перетворюють значення на логічне - корисно, коли функція має повертати саме true/false.

Докладніше в документації: Falsy

Універсального способу немає - для різних типів різні інструменти.

typeof - для примітивів і функцій:

typeof 'x';         // 'string'
typeof 1;           // 'number' (і для NaN теж)
typeof 1n;          // 'bigint'
typeof undefined;   // 'undefined'
typeof Symbol();    // 'symbol'
typeof (() => {});  // 'function'
typeof null;        // 'object' - історична помилка
typeof [];          // 'object'

Масив: Array.isArray(value), а не typeof чи instanceof.

instanceof - чи є в ланцюжку прототипів конструктор: err instanceof TypeError. Пастка - різні realm'и: масив з iframe чи з іншого vm-контексту в Node.js має інший Array, і instanceof Array дає false. Array.isArray таку проблему не має.

Точний «внутрішній» тип:

Object.prototype.toString.call(new Date());  // '[object Date]'
Object.prototype.toString.call(null);        // '[object Null]'

Інші корисні перевірки:

  • Number.isNaN(x), Number.isInteger(x), Number.isFinite(x) - на відміну від глобальних isNaN/isFinite, не приводять аргумент до числа.
  • value === null - для null.
  • value !== null && typeof value === 'object' - «справжній» об'єкт.

Для даних ззовні (відповідь API, localStorage, форма) ручні перевірки швидко стають громіздкими. Тут використовують схеми валідації (Zod, Valibot): вони одночасно перевіряють дані під час виконання й дають TypeScript-тип.

Докладніше в документації: typeof

Прочитати - ще не значить знати

20 питань, по одному на екран, ~20 хв. Після завершення - розбір кожної помилки з посиланням на питання.