На відміну від багатьох інших мов, у JavaScript є дві різні сутності, які позначають відсутність (змістовного) значення.
Це null та undefined.
nullПримітивне значення null використовують як навмисне «порожнє значення» для змінної.
В інших мовах подібну конструкцію можна було б використати лише для (відсутніх) обʼєктів або типів-вказівників.
У JavaScript null загалом позначає порожнє значення для будь-якого типу.
let name = null;
// name is intentionally set to "empty" because it is not
// available
Перевірити, чи змінна дорівнює null, можна за допомогою оператора суворої рівності ===.
Хоча null є примітивним значенням, оператор typeof «помилково» повертає object з історичних причин.
Це означає, що його не можна використовувати самого по собі, щоб перевірити, чи змінна дорівнює null.
let name = null;
name === null;
// => true
// Pitfall:
typeof name;
// => 'object'
undefinedЗмінна, якій не присвоєно значення, має тип
undefined.1
Отже, якщо null позначає порожнє значення (але все ж таки значення), то undefined позначає повну відсутність значення. 🤯
undefined виникає в різних контекстах.
undefined.undefined.undefined.undefined, якщо тільки цей аргумент не має типового значення.let name;
console.log(name);
// => undefined
let obj = { greeting: 'hello world' };
console.log(obj.missingKey);
// => undefined
function returnNothing() {
return;
}
console.log(returnNothing());
// => undefined
Перевірити, чи змінна має значення undefined, можна за допомогою оператора суворої рівності === або оператора typeof.
let name;
name === undefined;
// => true
typeof name === 'undefined';
// => true
Не рекомендуємо присвоювати undefined змінній вручну: натомість завжди використовуймо null, щоб було зрозуміло, що порожнє значення встановлено навмисно.
Як зазначалося вище, у JavaScript звернення до неіснуючого ключа в обʼєкті повертає undefined.
Однак якщо спробувати отримати вкладене значення, а батьківського ключа не існує, то обчислення вкладеного ключа виконується над undefined і призводить до TypeError: Cannot read property ... of undefined.
Теоретично завжди потрібно було б перевіряти, чи існує батьківський ключ, перш ніж намагатися отримати вкладений ключ.
Часто це робили за допомогою оператора && (логічне і), але для глибоко вкладених значень це призводить до дуже довгих виразів.
obj.level1 && obj.level1.level2 && obj.level1.level2.level3;
Щоб розвʼязати цю проблему, 2020 року до специфікації мови додали опціональний ланцюжок.
За допомогою оператора опціонального ланцюжка ?. можна зробити так, щоб JavaScript намагався звернутися до вкладеного ключа лише тоді, коли батьківський не є null або undefined.
Інакше повертається undefined.
const obj = {
address: {
street: 'Trincomalee Highway',
city: 'Batticaloa',
},
};
obj.residence;
// => undefined
obj.address.zipCode;
// => undefined
obj.residence.street;
// => TypeError: Cannot read property 'street' of undefined
obj.residence?.street;
// => undefined
obj.residence?.street?.number;
// => undefined
null
Бувають ситуації, коли потрібно застосувати типове значення на випадок, якщо змінна дорівнює null або undefined.
Раніше це часто робили за допомогою тернарного оператора ? або використовуючи ліниве обчислення оператора || (логічне або).
Це має той недолік, що типове значення застосовується в усіх випадках, коли значення змінної є неправдивим (наприклад, '' або 0), а не лише тоді, коли вона дорівнює null або undefined.
Це легко може спричинити несподівані наслідки.
let amount = null;
amount = amount || 1;
// => 1
amount = 0;
amount = amount || 1;
// => 1
amount = 0;
amount ? amount : 1;
// => 1
Щоб розвʼязати цю проблему, ввели оператор обʼєднання з null ??.
Як і опціональний ланцюжок, його додали до специфікації мови 2020 року.
Оператор обʼєднання з null ?? повертає правий операнд лише тоді, коли лівий операнд дорівнює null або undefined.
Інакше повертається лівий операнд.
Завдяки цьому типове значення тепер можна застосовувати точніше.
let amount = null;
amount = amount ?? 1;
// => 1
amount = 0;
amount = amount ?? 1;
// => 0
[1] Undefined, MDN. (2021). https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined (дата звернення: 4 червня 2021 р.).