Інформація в цій статті корисна для розуміння старих скриптів.
Зараз так код не пишуть.
У першому розділі про змінні, ми ознайомились з трьома способами оголошення змінних:
let
const
var
Оголошення за допомогою var
подібне до let
. У більшості випадків ми можемо замінити let
на var
або навпаки і очікувати, що це буде працювати:
var message = "Привіт";
alert(message); // Привіт
Але var
– це зовсім інший звір, який походить з дуже давніх часів. Зазвичай він не використовується в сучасних скриптах, але все ще може переховуватися у старих.
Якщо ви не плануєте працювати з такими скриптами, ви можете навіть пропустити цей розділ або відкласти його.
З іншого боку, важливо розуміти відмінності при міграції старих скриптів з var
на let
, щоб уникати зайвих помилок.
Для “var” не існує блочної області видимості
Змінні, оголошені за допомогою var
, мають або функціональну, або глобальну область видимості. Вони видимі за межами блоку.
Наприклад:
if (true) {
var test = true; // використовуємо "var" замість "let"
}
alert(test); // true, змінна існує поза блоком if
Так як var
ігнорує блоки, ми отримали глобальну змінну test
.
Якщо б ми використали let test
замість var test
, тоді змінна була б видима тільки всередині if
:
if (true) {
let test = true; // використовуємо "let"
}
alert(test); // ReferenceError: test не визначена
Те саме і для циклів: змінна, оголошена за допомогою var
, не може бути блочною або локальною всередині цикла:
for (var i = 0; i < 10; i++) {
var one = 1;
// ...
}
alert(i); // 10, "i" видима за межами циклу, це глобальна змінна
alert(one); // 1, "one" видима за межами циклу, це глобальна змінна
Якщо блок коду знаходиться всередині функції, тоді var
стає змінною рівня функції:
function sayHi() {
if (true) {
var phrase = "Привіт";
}
alert(phrase); // спрацьовує
}
sayHi();
alert(phrase); // ReferenceError: phrase не визначена
Як ми бачимо, var
виходить за межі if
, for
або інших блоків коду. Так відбувається тому, що колись блоки у Javascript не мали лексичного середовища, тому var
– це пережиток минулого.
“var” терпить повторні оголошення
Якщо ми оголосимо одну і ту ж змінну за допомогою let
двічі в одній області видимості, матимемо помилку:
let user;
let user; // SyntaxError: 'user' вже оголошена
З var
, ми можемо повторно оголошувати змінну безліч разів. Якщо ми використовуємо var
із вже оголошенною змінною, воно просто ігнорується:
var user = "Петро";
var user = "Іван"; // цей "var" нічого не робить (змінна вже оголошена). Зміниться лише значення
// ...помилки не виникне
alert(user); // Іван
Змінні “var” можуть бути оголошені після їх використання
Оголошення за допомогою var
обробляються при запуску функції (або скрипта для глобальних змінних).
Іншими словами, змінні var
визначаються на початку функції, незалежного від того, де саме знаходиться визначення (припускаючи, що визначення не перебуває у вкладеній функції).
Отже, цей код:
function sayHi() {
phrase = "Привіт";
alert(phrase);
var phrase;
}
sayHi();
…технічно такий самий, як і цей (var phrase
переміщена вище):
function sayHi() {
var phrase;
phrase = "Привіт";
alert(phrase);
}
sayHi();
…Або навіть як цей (пам’ятайте, блоки коду ігноруються):
function sayHi() {
phrase = "Привіт"; // (*)
if (false) {
var phrase;
}
alert(phrase);
}
sayHi();
Таку поведінку називають “підняттям”, оскільки всі var
“піднімаються” на початок функції.
Отже, у прикладі, наведеному вище, if (false)
не виконується, але це не має значення. var
всередині нього обробляється на початку функції, так що у момент (*)
змінна існує.
Оголошення змінних піднімаються, але присвоєння значень – ні.
Краще продемонструвати це на прикладі:
function sayHi() {
alert(phrase);
var phrase = "Привіт";
}
sayHi();
Рядок var phrase = "Привіт"
складається з двох дій:
- Оголошення змінної
var
- Присвоєння значення змінній
=
.
Оголошення обробляється на початку виконання функції (“піднімається”), однак присвоєння значень завжди відбувається в тому рядку коду, де воно вказано. Отже, код працює наступним чином:
function sayHi() {
var phrase; // оголошення змінної спрацьовує спочатку...
alert(phrase); // undefined
phrase = "Привіт"; // ...присвоєння - в момент, коли виконується даний рядок коду.
}
sayHi();
Оскільки всі оголошення var
обробляються при запуску функції, ми можемо посилатися на них у будь-якому місці. Але всі змінні мають значення undefined до оголошення.
В обох прикладах, наведених вище, alert
спрацьовує без помилок, тому що змінна phrase
існує. Але значення їй ще не було присвоєне, тому воно показує undefined
.
IIFE
Раніше, оскільки існував тільки var
, і він не мав видимості на рівні блоків, програмісти знайшли спосіб емулювати її. Те, що вони зробили, мало назву “вирази функцій, що викликаються негайно” (immediately-invoked function expressions – IIFE).
Сьогодні це не слід використовувати, але це можна знайти у старих скриптах.
IIFE виглядає так:
(function() {
var message = "Привіт";
alert(message); // Привіт
})();
Тут створюється і негайно викликається Function Expression (Функціональний вираз). Таким чином, код виконується відразу і має власні приватні змінні.
Function Expression обгортається дужками(function {...})
, тому що коли рушій JavaScript зустрічає "function"
в основному коді, він розуміє це як початок Function Declaration. Але Function Declaration повинна мати назву, тому такий код викличе помилку:
// Намагається оголосити і негайно викликати функцію
function() { // <-- SyntaxError: Оператори функцій вимагають назви
var message = "Привіт";
alert(message); // Привіт
}();
Навіть якщо ми скажемо: “добре, додамо назву”, це не спрацює, оскільки JavaScript не дозволяє негайно викликати Function Declarations:
// синтаксична помилка через дужки нижче
function go() {
}(); // <-- не може негайно викликати Function Declaration
Отже, дужки навколо функції – це хитрість, щоб показати JavaScript, що функція створена в контексті іншого виразу, а отже, це Function Expression: вона не потребує назви і її можна викликати негайно.
Крім дужок, існують інші способи повідомити JavaScript, що ми маємо на увазі Function Expression:
// Способи створення IIFE
(function() {
alert("Дужки навколо функції");
})();
(function() {
alert("Круглі дужки навколо всього");
}());
!function() {
alert("Побітовий оператор NOT запускає вираз");
}();
+function() {
alert("Унарний плюс запускає вираз");
}();
У всіх вищенаведених випадках ми оголошуємо Function Expression і негайно запускаємо його. Зауважимо ще раз: нині немає причин писати такий код.
Підсумки
Існує дві основні відмінності між var
та let/const
:
- Змінні
var
не мають блочної області видимості, їх видимість визначається поточною функцією або глобально, якщо оголошені поза функцією. - Оголошення за допомогою
var
обробляються при запуску функції (або скрипта для глобальних змінних).
Існує ще одна дуже незначна відмінність, пов’язана з глобальним об’єктом, яку ми розглянемо в наступному розділі.
Ці відмінності роблять var
гірше, ніж let
у більшості випадків. Блочна область видимості – гарна річ. Ось чому let
давно був введений до стандарту і тепер є основним способом (разом з const
) оголошування змінних.