Різниця між null та undefined
null та undefined - обидва позначають "немає значення" в JavaScript, але перший з'являється автоматично, а другий твій код встановлює явно.
Теорія
TL;DR
undefinedJavaScript-рушій встановлює сам, коли змінна оголошена але не отримала значенняnullце значення яке ти пишеш явно, щоб сказати "тут навмисно нічого"- Аналогія:
undefined— порожня поштова скринька (місце є, листа немає);null— записка всередині: "навмисно порожньо" - Обидва є falsy, тому
if (!value)спіймає обидва. Щоб розрізнити, використовуй суворе порівняння (===) - Правило: нехай
undefinedсигналізує про неініціалізований стан;nullвиставляй коли навмисно очищуєш значення
Швидкий приклад
let uninitialized; // undefined за замовчуванням
console.log(uninitialized); // undefined
let cleared = "hello";
cleared = null; // явне скидання
console.log(cleared); // null
console.log(typeof uninitialized); // "undefined"
console.log(typeof cleared); // "object" (спадковий баг з 1995-го)typeof null повертає "object", а не "null". Це відомий баг JavaScript з 1995 року, який залишили щоб не зламати існуючий код.
Ключова різниця
undefined з'являється без твоїх дій. Оголоси змінну без присвоєння, зверни до відсутньої властивості об'єкта, виклич функцію без return — рушій підставить undefined. null твій код має встановити явно. Це сигнал: поле існує і воно навмисно порожнє. Різниця важлива в API та моделях даних, де "не існує" і "навмисно відсутнє" не одне й те саме.
Коли використовувати
- Змінна оголошена але ще не присвоєна: залиш як є, рушій подбає сам
- Властивість об'єкта яку хочеш очистити: встанови
null - JSON-відповідь API для відсутнього поля: використовуй
null(undefinedповністю випадає зJSON.stringify) - Функція без явного
return: повертаєundefinedза замовчуванням - Дані сесії після виходу з системи:
req.user = null— свідоме очищення, не пропущена ініціалізація
Порівняльна таблиця
| Аспект | undefined | null |
|---|---|---|
| Походження | Автоматично (рушій) | Явно (твоє присвоєння) |
typeof | "undefined" | "object" |
Нестроге порівняння == | Рівне null | Рівне undefined |
Строге порівняння === | Рівне лише собі | Рівне лише собі |
JSON.stringify | Властивість повністю випадає | Серіалізується як "null" |
| Типове використання | Відсутні властивості, неініціалізовані змінні | Навмисно порожні поля, API-відповіді |
Як рушій це обробляє
Під час фази створення контексту виконання (execution context) V8 присвоює undefined кожній оголошеній змінній до запуску будь-якого коду. Саме тому читання var до рядка її присвоєння дає undefined замість помилки. null зберігається внутрішньо як тегований вказівник на порожній слот, через що typeof null повертає "object" замість "null".
Типові помилки
Перевірка через == коли потрібна точність
let val = null;
if (val == null) console.log('знайшли'); // true, але спіймає і undefinednull == undefined дає true. Якщо потрібно спіймати лише null, використовуй val === null. Якщо обидва прийнятні, val == null підійде — але пам'ятай що він спіймає і те, і інше.
Очікування що typeof null поверне "null"
if (typeof foo === 'null') {} // ніколи не виконаєтьсяДля перевірки null завжди використовуй строге порівняння: foo === null.
undefined тихо зникає з JSON
JSON.stringify({ a: undefined, b: null });
// Результат: '{"b":null}'Це класична пастка для бекенд-команд. Ендпоінт повертає { field: undefined } вважаючи що поле є, але клієнт отримує {}. Якщо поле має з'являтись у відповіді навіть порожнім, використовуй null.
Нестроге порівняння приховує реальний стан
const data = { user: { profile: null } };
if (data.user.profile == undefined) {
console.log('safe'); // спрацьовує, бо null == undefined
}Умова виконується і маскує факт що profile явно встановлено в null. Для точності використовуй === undefined.
Де зустрічається у реальному коді
- React:
useState(null)як початковий стан до завантаження даних - Express:
req.user = nullпісля виходу — явне очищення сесії - Redux:
state.error = nullдля скидання помилки між запитами (зберіганняundefinedспричиняє застарілі повідомлення які не очищаються між dispatch-ами) - Node.js
fs.readFile: параметрerrу callback дорівнюєnullпри успіху, неundefined - Lodash:
_.isNil(value)перевіряєnull || undefinedколи обидва прийнятні
Питання на співбесіді
Q: Що повертає typeof null і чому?
A: Повертає "object". В оригінальній C-реалізації JavaScript null мав той самий тег типу що й об'єкти (0x00). Баг не виправили щоб не зламати існуючий код.
Q: Як null та undefined поводяться в умовних виразах?
A: Обидва є falsy, тому if (!value) спіймає обидва. Для розрізнення використовуй строге порівняння: value === null або value === undefined.
Q: Що відбувається з undefined в JSON?
A: JSON.stringify повністю прибирає властивості зі значенням undefined. null серіалізується як "null". Тому API використовують null для відсутніх даних.
Q: Що таке void 0 і навіщо деякі мініфайкери його використовують?
A: void 0 завжди обчислюється як undefined. У старому JavaScript без strict mode глобальний undefined можна було перезаписати. void 0 — безпечна альтернатива. В сучасному коді зі strict mode це не потрібно.
Q: Як відрізнити відсутній ключ від ключа зі значенням undefined?
A: Обидва повертають undefined при зверненні. Але 'key' in obj дає false для відсутнього ключа і true для ключа явно встановленого в undefined. Ось як їх розрізнити.
Приклади
Базовий: автоматичне проти явного присвоєння
// undefined: рушій встановлює сам, без твоїх дій
let username;
console.log(username); // undefined
function greet(name) {
console.log(name);
}
greet(); // undefined - аргумент не передано
// null: ти встановлюєш навмисно
let currentUser = null; // ще не авторизований
console.log(currentUser); // nullundefined з'являється коли щось відсутнє або не присвоєне. null є в коді лише тоді коли ти його туди поклав.
Реальний сценарій: Express-обробник та JSON-відповідь
app.get('/user/:id', (req, res) => {
const user = db.getUser(req.params.id);
if (user === undefined) {
// db.getUser нічого не знайшла — не те саме що "користувач без ролі"
return res.status(404).json({ error: 'User not found' });
}
// Явно очищуємо чутливе поле перед відправкою
res.json({ ...user, passwordHash: null });
});
// GET /user/123 (є): { id: 123, name: "Alice", passwordHash: null }
// GET /user/999 (нема): 404, { error: "User not found" }db.getUser повертає undefined коли запис не знайдено. Відповідь встановлює passwordHash в null, щоб поле залишилось у JSON-відповіді але не містило чутливих даних.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.