Var, let, const, в чём разница? И как использовать?
Coding
До появления стандарта ECMAScript 6 у нас в распоряжении было только одно ключевое слово для объявления переменных — var.
Переменные, объявленные с помощью var, могут находиться:
- Либо в области видимости функции, если они объявлены внутри функции. В этом случае они доступны только внутри функции, в которой объявлены.
- Либо в глобальной области видимости, если они объявлены вне функции. В таком случае, они доступны где угодно.
/* глобальная переменная */
var test = 100;
(() => {
/* локальная переменная, доступна только внутри функции */
var test = 200;
console.log(test); //200
})();
console.log(test); //100
В примере в первом случае переменная test объявлена в глобальной области видимости, а во втором — в локальной. Первый console.log выведет значение локальной переменной, а второй — глобальной.
Переменной, объявленной с помощью var, можно присвоить новое значение:
var test = 100; console.log(test); //100 test = 200; console.log(test); //200
И наконец, переменную, объявленную с помощью var, можно переобъявить, т.е. создать другую переменную с тем же именем в той же области видимости:
var test = 100; console.log(test); //100 var test = 200; console.log(test); //200
А теперь вернемся к первому примеру и выведем значение переменной, скажем, здесь:
var test = 100;
(() => {
console.log(test); //undefined
var test = 200;
console.log(test); //200
})();
console.log(test); //100
Новый console.log выведет значение undefined. Это явление называется "подъемом переменных".
Интерпретатор JavaScript незаметно для нас «поднимает» объявления переменных в начало области видимости. В нашем случае, это начало функции.
Переменные, объявленные с помощью var, инициализируются значением undefined.
В ES6 появились два новых ключевых слова: let и const.
let
Ключевое слово let очень похоже на var, но все же имеет некоторые отличия. Начнем с того, что переменная, объявленная с помощью let попадает в область видимости уровня блока. Напомню, что блоком является часть кода, заключенная в фигурные скобки.
Взгляните на пример:
/* глобальная переменная */
let test = 100;
{
/* локальная переменная, доступна только внутри блока */
let test = 200;
console.log(test); //200
}
console.log(test); //100
Здесь первая переменная test объявлена в глобальной области видимости, а вторая — в локальной области видимости уровня блока.
Переменным, объявленным с помощью let, можно присвоить новое значение.
let test = 100; console.log(test); //100 test = 200; console.log(test); //200
Но, в отличии от var, нельзя объявить еще одну переменную с тем же именем в той же области видимости:
let test = 100; console.log(test); let test = 200; console.log(test); Uncaught SyntaxError: Identifier 'test' has already been declared
При попытке переобъявить переменную, мы получим ошибку.
Зато каждый цикл создает свою область видимости, поэтому переменные с одним и тем же именем можно использовать в разных циклах:
for (let i = 0; i < 10; i++) { /* … */ }
for (let i = 0; i < 10; i++) { /* … */ }
Кстати, стоит отметить, что объявление цикла и тело цикла образуют две области видимости.
let i = 100;
for (let i = 0; i < 10; i++) {
let i = 200;
console.log(i); //200
}
В примере в консоль десять раз выведется значение 200.
Такое поведение let позволяет решить классическую задачу с замыканиями.
for (var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 10);
}
C var в примере мы получим в консоли десять раз значение 10. Но если заменим var на let, пример будет работать так, как и ожидается, и мы получим в консоли числа от 0 до 9.
for (let i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 10);
}
К объявлениям let также применяется подъем переменных, но сами переменные, в отличии от var, недоступны, пока не будут определены.
{
/* переменная test здесь не инициализирована. Попытка доступа вызовет ошибку */
let test = 5;
// test равна 5
}
Это называется временная мертвая зона (temporal dead zone) переменной.
const
Ключевое слово const полностью аналогично let, но переменной, объявленной с помощью const, нельзя присвоить новое значение и нельзя объявить неинициализированную константу.
const test = 1; test = 2; Uncaught TypeError: Assignment to constant variable.
Тут стоит отметить, что если константой является объект, то изменять его свойства все же можно.
const foo = {};
foo.bar = 42;
console.log(foo.bar);
Нельзя присвоить константе другой объект.
Итак, подытожим.
Переменные, объявленные с помощью var, попадают в область видимости уровня функции, их можно изменять и переобъявлять.
Переменные, объявленные с помощью let, попадают в область видимости уровня блока, их можно изменять, но нельзя переобъявлять.
И наконец, переменные, объявленные с помощью const, попадают в область видимости уровня блока, их нельзя изменять или переобъявлять.
Какое же из ключевых слов использовать и в каком случае? Здесь мнения расходятся. Кто-то считает, что по умолчанию следует использовать const, let использовать только в том случае, если значение переменной должно обновляться, а var не стоит использовать вовсе, ну или в крайнем случае, в легаси-проектах.
А кто-то предпочитает по-старинке объявлять переменные с помощью var, а let использовать только тогда, когда переменная действительно используется исключительно в блоке.