Переменные: let и const
Материал на этой странице устарел, поэтому скрыт из оглавления сайта.
В ES-2015 предусмотрены новые способы объявления переменных: через let и const вместо var .
let a = 5;
let
У объявлений переменной через let есть три основных отличия от var :
-
Область видимости переменной let – блок <. >. Как мы помним, переменная, объявленная через var , видна везде в функции. Переменная, объявленная через let , видна только в рамках блока <. >, в котором объявлена. Это, в частности, влияет на объявления внутри if , while или for . Например, переменная через var :
var apples = 5; if (true) < var apples = 10; alert(apples); // 10 (внутри блока) >alert(apples); // 10 (снаружи блока то же самое)
В примере выше apples – одна переменная на весь код, которая модифицируется в if . То же самое с let будет работать по-другому:
let apples = 5; // (*) if (true) < let apples = 10; alert(apples); // 10 (внутри блока) >alert(apples); // 5 (снаружи блока значение не изменилось)
Здесь, фактически, две независимые переменные apples , одна – глобальная, вторая – в блоке if . Заметим, что если объявление let apples в первой строке (*) удалить, то в последнем alert будет ошибка: переменная не определена:
if (true) < let apples = 10; alert(apples); // 10 (внутри блока) >alert(apples); // ошибка!
alert(a); // undefined var a = 5;
С переменными let всё проще. До объявления их вообще нет. Такой доступ приведёт к ошибке:
alert(a); // ошибка, нет такой переменной let a = 5;
Заметим также, что переменные let нельзя повторно объявлять. То есть, такой код выведет ошибку:
let x; let x; // ошибка: переменная x уже объявлена
Это – хоть и выглядит ограничением по сравнению с var , но на самом деле проблем не создаёт. Например, два таких цикла совсем не конфликтуют:
// каждый цикл имеет свою переменную i for(let i = 0; i for(let i = 0; i alert( i ); // ошибка: глобальной i нет
for(var i=0; i alert(i); // 10
С переменной let – всё по-другому. Каждому повторению цикла соответствует своя независимая переменная let . Если внутри цикла есть вложенные объявления функций, то в замыкании каждой будет та переменная, которая была при соответствующей итерации. Это позволяет легко решить классическую проблему с замыканиями, описанную в задаче Армия функций.
function makeArmy() < let shooters = []; for (let i = 0; i < 10; i++) < shooters.push(function() < alert( i ); // выводит свой номер >); > return shooters; > var army = makeArmy(); army[0](); // 0 army[5](); // 5
const
Объявление const задаёт константу, то есть переменную, которую нельзя менять:
const apple = 5; apple = 10; // ошибка
В остальном объявление const полностью аналогично let .
Заметим, что если в константу присвоен объект, то от изменения защищена сама константа, но не свойства внутри неё:
const user = < name: "Вася" >; user.name = "Петя"; // допустимо user = 5; // нельзя, будет ошибка
То же самое верно, если константе присвоен массив или другое объектное значение.
константы и КОНСТАНТЫ
Константы, которые жёстко заданы всегда, во время всей программы, обычно пишутся в верхнем регистре. Например: const ORANGE = «#ffa500» .
Большинство переменных – константы в другом смысле: они не меняются после присвоения. Но при разных запусках функции это значение может быть разным. Для таких переменных можно использовать const и обычные строчные буквы в имени.
Итого
- Видны только после объявления и только в текущем блоке.
- Нельзя переобъявлять (в том же блоке).
- При объявлении переменной в цикле for(let …) – она видна только в этом цикле. Причём каждой итерации соответствует своя переменная let .
Переменная const – это константа, в остальном – как let .
Let js что это
В Javascript ключевое слово var относится к функциональной области видимости. Этим javascript отличается от многих языков программирования (C# / Java и т. д.), в которых переменные имеют блочную область видимости. Если представить блочную видимость в JavaScript, то в примере ниже мы ожидаем, что в консоль попадет 123 , однако вместо этого будет выведено 456 :
1 2 3 4 5
var foo = 123; if (true) var foo = 456; > console.log(foo); // 456
Так происходит, потому что < не создает новую область видимости переменных. Переменная foo , которая находится внутри if, ведет себя так же, как если бы она была объявлена вне его. Это поведение часто является источником ошибок в JavaScript. По этой причине TypeScript (и ES6) добавили ключевое слово let , которое позволяет нам объявлять переменные с настоящей блочной областью видимости.
Таким образом, если вы используете ключевое слово let вместо var , вы получите по настоящему уникальный элемент, который не будет взаимодействовать с областями видимости, которые располагаются вне его области видимости. Ниже мы переписали предыдущий пример, используя let :
1 2 3 4 5
let foo = 123; if (true) let foo = 456; > console.log(foo); // 123
Еще один пример, где let может защитить вас от ошибок.
1 2 3 4 5 6
var index = 0; var array = [1, 2, 3]; for (let index = 0; index array.length; index++) console.log(array[index]); > console.log(index); // 0
Честно говоря, мы считаем, что лучше использовать let , когда это возможно, так как это приводит к меньшим сюрпризам для новых и существующих разработчиков.
Функции создают новую область видимости¶
Поскольку мы упоминали об этом, мы хотели бы продемонстрировать, что функции создают новую область видимости переменных в JavaScript:
1 2 3 4 5 6
var foo = 123; function test() var foo = 456; > test(); console.log(foo); // 123
Это ведет себя так, как вы ожидаете. Без этого было бы очень сложно писать код на JavaScript.
Сгенерированный JS¶
JS код, генерируемый TypeScript, является простым переименованием переменной let , если подобное имя уже существует в окружающей области видимости. Например. следующий код генерируется как есть с простой заменой var на let :
1 2 3 4 5 6 7 8 9
if (true) let foo = 123; > // становится // if (true) var foo = 123; >
Однако, если имя уже используется в окружающей области видимости, то typescript сгенерирует следующий код:
1 2 3 4 5 6 7 8 9 10 11
var foo = '123'; if (true) let foo = 123; > // становится // var foo = '123'; if (true) var foo_1 = 123; // просто переименовываем >
Switch¶
Мы можем обернуть тело case , используя <> для переиспользования имени переменной в других case выражениях, как показано ниже:
1 2 3 4 5 6 7 8 9 10 11 12
switch (name) case 'x': let x = 5; // . break; > case 'y': let x = 10; // . break; > >
let в замыканиях¶
Самый частый вопрос на собеседовани — какой будет результат при исполнении следующего кода:
1 2 3 4 5 6 7 8 9 10 11
var funcs = []; // создаем список функций for (var i = 0; i 3; i++) funcs.push(function () console.log(i); >); > // вызываем их for (var j = 0; j 3; j++) funcs[j](); >
Начинающие разработчики часто говорят, что результатом будет 0,1,2 . Однако очень сильно удивляются, когда узнают, что этот ответ неправильный, а результатом исполнения функции будет 3 во всех трех функциях. Так происходит, потому что все эти функции выводят значение переменной i из окружающей области видимости, в момент вызова этих функций она равняется 3.
Одним из возможных вариантов исправления этой ошибки является создание функции для каждой итерации цикла. Как мы узнали ранее, мы можем создать новую область видимости с помощью немедленно вызывающейся функции (паттерн IIFE (function() < /* body */ >)(); ):
1 2 3 4 5 6 7 8 9 10 11 12 13 14
var funcs = []; // создаем список функций for (var i = 0; i 3; i++) (function () var local = i; funcs.push(function () console.log(local); >); >)(); > // вызываем их for (var j = 0; j 3; j++) funcs[j](); >
В этом примере функция замыкает (поэтому ее и называют замыкание ) локальные переменные (по соглашению называемые local ) и использует их вместо i , которая используется в цикле.
Обратите внимание, что замыкания влияют на производительность (но они нам нужны для сохранения окружающего состояния).
В ES6 ключевое слово let в цикле будет иметь такое же поведение, как и в примере ранее:
1 2 3 4 5 6 7 8 9 10 11 12
var funcs = []; // создаем список инструкций for (let i = 0; i 3; i++) // используем let funcs.push(function () console.log(i); >); > // вызываем for (var j = 0; j 3; j++) funcs[j](); >
Использование let вместо var создает переменную i , уникальную для каждой итерации цикла.
Резюме¶
let — нереально полезный инструмент, который рекомендуется использовать как можно чаще. Он может существено повысить читабельность и предсказуемость вашего кода и снизить количество допускаемых ошибок.
В чём разница между var и let
Если вы недавно пишете на JavaScript, то наверняка задавались вопросом, чем отличаются var и let , и что выбрать в каждом случае. Объясняем.
var и let — это просто два способа объявить переменную. Вот так:
var x = 10; let y = 20;
Переменная, объявленная через var , доступна только внутри «своей» функции, или глобально, если она была объявлена вне функции.
function myFunction() < var z = 30; console.log(z); // 30 >myFunction(); console.log(z); // ReferenceError
Это может создавать неожиданные ситуации. Допустим, вы создаёте цикл в функции и хотите, чтобы переменная i осталась в этой функции. Если вы используете var , эта переменная «утечёт» за пределы цикла и будет доступна во всей функции.
Переменные, объявленные с помощью let доступны только в пределах блока кода, в котором они были объявлены.
if (true) < let a = 40; console.log(a); // 40 >console.log(a); // ReferenceError
В JavaScript блок кода — это участок кода, заключённый в фигурные скобки <> . Это может быть цикл, код в условном операторе или что-нибудь ещё.
if (true) < let blockScoped = "Я виден только здесь"; console.log(blockScoped); // "Я виден только здесь" >// здесь переменная blockScoped недоступна console.log(blockScoped); // ReferenceError
Если переменная j объявлена в цикле с let , она останется только в этом цикле, и попытка обратиться к ней за его пределами вызовет ошибку.
Поднятие
В JavaScript есть механизм поднятия (hoisting), при котором объявления переменных перемещаются в начало их области видимости при выполнении кода. Это работает по-разному для var и let .
Например, вы выводите значение переменной в консоль, и она вернёт undefined , если инициализация произойдёт позже.
// Как JavaScript видит ваш код var hoistedVar; console.log(hoistedVar); // undefined hoistedVar = "Теперь я определен!";
С let такого не произойдёт; любая попытка обратиться к переменной до её объявления вызовет ошибку.
// Этот код вызовет ошибку console.log(hoistedLet); // ReferenceError let hoistedLet = "Теперь я определен!";
Когда использовать var и let
Когда выбрать var
- Когда нужна переменная с функциональной областью видимости или глобальная переменная (хотя это не очень хорошая практика).
- Когда работаете с устаревшим кодом, где уже используется var .
Когда выбрать let
- Когда вам нужна переменная с блочной областью видимости, которую не нужно тянуть в другие части программы.
- Когда вы хотите избежать неожиданного поведения, связанного с поднятием.
В современной разработке и новых проектах разработчики предпочитают let и const из-за их предсказуемости и надёжности.
«Доктайп» — журнал о фронтенде. Читайте, слушайте и учитесь с нами.
Ключевое слово let
В редакцию ES2015 были введены два новых важных ключевых слова JavaScript — let и const.
Эти два ключевых слова позволяют в JavaScript декларировать переменные и константы с областью видимости внутри блока.
До редакции ES2015 в JavaScript было только два типа области видимости — глобальная и внутри функции.
Глобальная область видимости
Переменные, декларированные глобально (вне какой-либо функции), обладают глобальной областью видимости.
var carName = "Volvo"; // расположенный здесь код может использовать переменную carName function myFunction() < // расположенный здесь код также может использовать переменную carName >
Доступ к глобальным переменным можно получить из любого места программы на JavaScript.
Область видимости внутри функции
Переменные, декларированные локально (внутри какой-либо функции), обладают областью видимости внутри функции.
// расположенный здесь код не может использовать переменную carName function myFunction() < var carName = "Volvo"; // расположенный здесь код может использовать переменную carName >
Доступ к локальным переменным можно получить только внутри той функции, где эти переменные были декларированы.
Область видимости внутри блока
Переменные, декларированные при помощи ключевого слова var, не обладают областью видимости внутри блока.
Т.е. доступ к таким переменным, декларированным внутри блока , можно получить и вне этого блока.
< var x = 2; >// здесь переменную x МОЖНО использовать
До редакции ES2015 в JavaScript не было области видимости внутри блока.
Начиная с редакции ES2015 переменные, декларированные при помощи ключевого слова let, приобретают область видимости внутри блока. В результате этого переменные, декларированные таким образом внутри блока , становятся недоступными за пределами этого блока.
< let x = 2; >// здесь переменную x НЕЛЬЗЯ использовать
Передекларирование переменных
Передекларирование переменной при помощи ключевого слова var может вызвать проблемы.
Так, если передекларировать переменную внутри блока, то она будет перекларирована и за его пределами:
var x = 10; // Здесь значение x равно 10 < var x = 2; // Здесь значение x равно 2 >// Здесь значение x равно 2
Решить эту проблему можно, если передекларировать переменную при помощи ключевого слова let.
В этом случае передекларирование переменной внутри блока не будет передекларировать переменную за пределами этого блока:
var x = 10; // Здесь значение x равно 10 < let x = 2; // Здесь значение x равно 2 >// Здесь значение x равно 10
Область видимости внутри цикла
Использование ключевого слова var в цикле:
var i = 5; for (var i = 0; i < 10; i++) < // какой-то код >// Здесь значение i равно 10
Использование ключевого слова let в цикле:
let i = 5; for (let i = 0; i < 10; i++) < // какой-то код >// Здесь значение i равно 5
В первом примере переменная, декларированная в цикле при помощи ключевого слова var, также передекрарирует переменную за пределами цикла.
Во втором примере переменная, декларированная в цикле с ключевым словом let, не передекрарирует переменную за пределами цикла, оставляя неизменным ее значение, так как использование ключевого слова let для декларирования переменной i в цикле делает переменную i видимой только внутри этого цикла.
Область видимости внутри функции
Поведение переменных, декларированных внутри функции при помощи ключевых слов var и let, одинаковое. В обоих случаях у переменных будет локальная область видимости.
function myFunction() < var carName = "Volvo"; // Область видимости внутри функции >function myFunction() < let carName = "Volvo"; // Область видимости внутри функции >
Глобальная область видимости
Поведение переменных, декларированных вне блока при помощи ключевых слов var и let, одинаковое. В обоих случаях у переменных будет глобальная область видимости.
var x = 2; // Глобальная область видимости let x = 2; // Глобальная область видимости
Глобальные переменные в HTML
В JavaScript глобальная область видимости распространяется на все окружение JavaScript.
В HTML глобальная область видимости — это глобальный объект window.
Глобальные переменные, декларированные при помощи ключевого слова var, принадлежат глобальному объекту window:
var carName = "Volvo"; // здесь код может использовать window.carName
Глобальные переменные, декларированные при помощи ключевого слова let, не принадлежат глобальному объекту window:
let carName = "Volvo"; // здесь код не может использовать window.carName
Передекларирование
Передекларировать переменную JavaScript при помощи ключевого слова var можно в любом месте программы:
var x = 2; // Значение x равно 2 var x = 3; // Теперь значение x равно 3
Нельзя передекларировать переменную, которая была декларирована при помощи ключевого слова var, при помощи ключевого слова let в той же области видимости или в том же блоке:
var x = 2; // Можно let x = 3; // Нельзя < var x = 4; // Можно let x = 5 // Нельзя >
Нельзя передекларировать переменную, которая была декларирована при помощи ключевого слова let, при помощи ключевого слова let в той же области видимости или в том же блоке:
let x = 2; // Можно let x = 3; // Нельзя < let x = 4; // Можно let x = 5; // Нельзя >
Нельзя передекларировать переменную, которая была декларирована с ключевым словом let, при помощи ключевого слова var в той же области видимости или в том же блоке:
let x = 2; // Можно var x = 3; // Нельзя < let x = 4; // Можно var x = 5; // Нельзя >
Передекларировать переменную при помощи ключевого слова let в другой области видимости или в другом блоке можно:
let x = 2; // Можно < let x = 3; // Можно > < let x = 4; // Можно >
Поднятие
Переменные, декларированные при помощи ключевого слова var, «поднимаются» в верх JavaScript кода (если вы не знаете, что такое «поднятие» в JavaScript, то см. главу Поднятие переменных в Javascript).
Таким образом, мы можем использовать такие переменные до того, как они будут декларированы:
// здесь МОЖНО использовать carName var carName;
Переменные, декларированные при помощи ключевого слова let, не «поднимаются» в верх JavaScript кода.
Использование переменной, декларированной при помощи ключевого слова let, до ее реальной декларации приведет к ошибке ReferenceError.
В этом случае такая переменная находится в, так называемой, «временной мертвой зоне» с начала блока и до ее декларации:
// здесь НЕЛЬЗЯ использовать carName let carName;