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').
У логічному контексті (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.
Універсального способу немає - для різних типів різні інструменти.
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-тип.
Спершу почитати
Прочитати - ще не значить знати
20 питань, по одному на екран, ~20 хв. Після завершення - розбір кожної помилки з посиланням на питання.