Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Область видимості var, let і const». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`var` має функціональну область видимості (function scope), а `let` і `const` - блочну (block scope), тобто живуть лише всередині `{ ... }`, де оголошені.** Усі три підіймаються (hoisting), але `var` до оголошення дає `undefined`, а `let` і `const` кидають `ReferenceError`, бо перебувають у тимчасовій мертвій зоні (TDZ). `var` можна повторно оголосити і переприсвоїти, `let` - лише переприсвоїти, `const` - ні те, ні інше (хоча об'єкт за посиланням мутувати можна). ```javascript 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(); ``` **Ключове:** за замовчуванням пишіть `const`, беріть `let`, коли значення треба змінювати, і не використовуйте `var` у новому коді.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**`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` - тільки в старому коді. ### Швидкий приклад ```javascript 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`: ```javascript console.log(a); // undefined var a = 10; ``` JavaScript «піднімає» саме оголошення `var a`, але не присвоєння `a = 10`. Приклад з `let` і `const`: ```javascript console.log(b); // ReferenceError let b = 20; ``` Хоча `b` теж підіймається, вона перебуває у «тимчасовій мертвій зоні» (TDZ): від початку області видимості до фактичного рядка оголошення. Будь-яке звернення в цьому проміжку кидає `ReferenceError`. ### Повторне оголошення і переприсвоєння | Ключове слово | Чи можна оголосити повторно? | | --- | --- | | `var` | Так, перезапише значення | | `let`, `const` | Ні, `SyntaxError` | ```javascript var x = 1; var x = 2; // дозволено console.log(x); // 2 let y = 1; let y = 2; // SyntaxError ``` | Ключове слово | Чи можна змінити значення? | | --- | --- | | `var` | Так | | `let` | Так | | `const` | Ні, але об'єкт усередині можна мутувати | ```javascript let name = 'Maria'; name = 'Oleh'; // дозволено const user = { name: 'Maria' }; user.name = 'Oleh'; // дозволено: мутуємо об'єкт user = {}; // TypeError: не можна переприсвоїти посилання ``` `const` фіксує саме прив'язку (binding), тобто посилання, а не вміст об'єкта. Щоб заморозити і вміст, потрібен `Object.freeze`. ### Приклад, що показує всі відмінності ```javascript 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` як на «оновлення» змінної: це мовчазне перезаписування, яке ховає помилки.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.