Область видимості var, let і const
var має функціональну область видимості, а let і const - блочну, тому змінна, оголошена через var усередині if або циклу, «витікає» у всю функцію, а let і const залишаються всередині своїх фігурних дужок. Крім області видимості вони відрізняються поведінкою при hoisting, можливістю повторного оголошення і можливістю переприсвоєння.
Теорія
TL;DR
var- функціональна область видимості (function scope),letіconst- блочна (block scope).- Підіймаються (hoisted) всі три, але
varініціалізується значеннямundefined, аletіconstдо рядка оголошення перебувають у TDZ і кидаютьReferenceError. - Повторне оголошення в тій самій області:
varдозволяє,letіconstдаютьSyntaxError. - Переприсвоєння:
varіletдозволяють,constзабороняє, але мутація об'єкта за посиланням дозволена. - Сучасна практика:
constза замовчуванням,letза потреби,var- тільки в старому коді.
Швидкий приклад
function test() {
if (true) {
var x = 1;
let y = 2;
const z = 3;
}
console.log(x); // 1
console.log(y); // ReferenceError
console.log(z); // ReferenceError
}
test();x доступний, бо var «ігнорує» блок if і піднімається на рівень функції. y і z видно тільки всередині if.
Область видимості: функція проти блоку
| Ключове слово | Область видимості |
|---|---|
var | Функціональна (function scope) |
let, const | Блочна (block scope) |
Функціональна область означає, що змінну видно у всій функції, де її оголошено, незалежно від блоку {}.
Блочна область означає, що змінну видно тільки всередині блоку { ... }, де її оголошено: тіло if, тіло циклу, будь-які фігурні дужки.
Hoisting і TDZ
Усі змінні підіймаються (hoisted) на початок своєї області видимості, але поводяться по-різному.
| Ключове слово | Підіймається | Чи можна використати до оголошення? |
|---|---|---|
var | так | так, але значення буде undefined |
let, const | так | ні, помилка через TDZ (temporal dead zone) |
Приклад з var:
console.log(a); // undefined
var a = 10;JavaScript «піднімає» саме оголошення var a, але не присвоєння a = 10.
Приклад з let і const:
console.log(b); // ReferenceError
let b = 20;Хоча b теж підіймається, вона перебуває у «тимчасовій мертвій зоні» (TDZ): від початку області видимості до фактичного рядка оголошення. Будь-яке звернення в цьому проміжку кидає ReferenceError.
Повторне оголошення і переприсвоєння
| Ключове слово | Чи можна оголосити повторно? |
|---|---|
var | Так, перезапише значення |
let, const | Ні, SyntaxError |
var x = 1;
var x = 2; // дозволено
console.log(x); // 2
let y = 1;
let y = 2; // SyntaxError| Ключове слово | Чи можна змінити значення? |
|---|---|
var | Так |
let | Так |
const | Ні, але об'єкт усередині можна мутувати |
let name = 'Maria';
name = 'Oleh'; // дозволено
const user = { name: 'Maria' };
user.name = 'Oleh'; // дозволено: мутуємо об'єкт
user = {}; // TypeError: не можна переприсвоїти посиланняconst фіксує саме прив'язку (binding), тобто посилання, а не вміст об'єкта. Щоб заморозити і вміст, потрібен Object.freeze.
Приклад, що показує всі відмінності
function demo() {
console.log(a); // undefined
// console.log(b); // ReferenceError
// console.log(c); // ReferenceError
var a = 1;
let b = 2;
const c = 3;
if (true) {
var a = 10; // та сама змінна!
let b = 20; // нова змінна
const c = 30; // нова змінна
console.log(a, b, c); // 10 20 30
}
console.log(a, b, c); // 10 2 3
}
demo();var a «витекла» з блоку і перезаписала зовнішнє значення, а let і const створили власні локальні змінні всередині блоку, тому зовнішні b і c залишились недоторканими.
Що використовувати на практиці
| Ключове слово | Коли використовувати |
|---|---|
let | Для змінних, значення яких треба міняти |
const | За замовчуванням, завжди, якщо не потрібно міняти посилання |
var | Ніколи, тільки для підтримки старого коду |
Підсумкова таблиця:
| Властивість | var | let | const |
|---|---|---|---|
| Область видимості | Функціональна | Блочна | Блочна |
| Підіймається | Так | Так | Так |
| Використання до оголошення | undefined | Помилка (TDZ) | Помилка (TDZ) |
| Повторне оголошення | Так | Ні | Ні |
| Переприсвоєння | Так | Так | Ні |
| Сучасний стандарт | Застарілий | Рекомендується | За замовчуванням |
Типові помилки
- Вважати, що
varусерединіifабоforстворює локальну змінну. Насправді вона належить усій функції і може перезаписати зовнішнє значення. - Плутати hoisting з «немає помилки».
letіconstтеж підіймаються, просто звернення до них у TDZ падає зReferenceError. - Думати, що
constробить значення незмінним. Він забороняє лише переприсвоєння змінної, а поля об'єкта і елементи масиву мутувати можна. - Використовувати
varу циклі разом з асинхронним кодом: усі ітерації бачитимуть одну змінну, тоді якletстворює нову прив'язку на кожній ітерації. - Покладатись на повторне оголошення
varяк на «оновлення» змінної: це мовчазне перезаписування, яке ховає помилки.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.