Menu
Русский

let, const и var в JavaScript: разница и что выбрать

Три способа объявить переменную в JavaScript — и почему в современном коде по умолчанию идёт const, при необходимости let, а var практически не встречается.

Три ключевых слова, одна задача

В JavaScript переменные можно объявить тремя способами: var, let и const. Все они привязывают имя к значению, но отличаются областью видимости, правилами переприсваивания и поведением до момента самого объявления. В современном коде по умолчанию берут const, let — когда значение должно меняться, а var почти не используют.

Если совсем коротко:

index.js
Output
Click Run to see the output here.

Оставшаяся часть страницы объясняет, почему эти три строки ведут себя именно так.

const — выбор по умолчанию

const создаёт привязку, которую нельзя переприсвоить. Если вы написали const x = 5, то позже сказать x = 6 уже не получится — движок выбросит TypeError.

index.js
Output
Click Run to see the output here.

Вот и всё правило. В хорошо написанном коде большинство переменных вообще не нужно переприсваивать, так что const отлично подходит в подавляющем большинстве случаев. А когда вы по умолчанию тянетесь к const, те места, где значение действительно меняется, сразу бросаются в глаза.

Типичная точка путаницы: const защищает саму привязку, а не значение. Если за переменной стоит объект или массив, его содержимое всё равно можно менять:

index.js
Output
Click Run to see the output here.

const означает «эта переменная всегда ссылается на один и тот же объект». Это вовсе не значит, что «объект никогда не меняется». Если нужно заморозить сам объект — для этого есть Object.freeze(user). Но на практике большинство разработчиков просто договариваются не мутировать объекты, объявленные через const.

let: когда действительно нужно переприсваивание

let ведёт себя точно так же, как const, с одним отличием — значение можно переприсваивать. Используйте let для счётчиков, аккумуляторов, переменных цикла и вообще везде, где значение реально меняется со временем.

index.js
Output
Click Run to see the output here.

Если заметили, что объявили переменную через let, но ни разу её не переприсваиваете — смело меняйте на const. В большинстве проектов линтер сам подскажет это сделать.

Блочная область видимости в JavaScript

И let, и const имеют блочную область видимости. Блок — это всё, что находится между { и }: тело if, for, функции или даже просто отдельный блок { ... }. Переменная, объявленная внутри блока, снаружи уже недоступна.

index.js
Output
Click Run to see the output here.

Именно такое поведение вам и нужно. Переменные остаются в пределах того блока, к которому относятся, а случайные конфликты имён становятся невозможными.

С var так не получится — и это главная причина, почему от него стоит отказаться.

var: функциональная область видимости и её сюрпризы

var живёт в ближайшей функции, а не в ближайшем блоке. То есть var, объявленный внутри if или for, «утекает» наружу и становится виден во всей окружающей функции:

index.js
Output
Click Run to see the output here.

Именно такая «размытая» область видимости — источник длинного списка классических багов JavaScript. Самый известный пример: цикл, в котором все итерации делят одну и ту же переменную var i, и в итоге все колбэки видят её финальное значение.

Ещё var спокойно позволяет повторно объявить переменную с тем же именем в той же области видимости — и это отлично маскирует опечатки:

index.js
Output
Click Run to see the output here.

Современный JS-код почти всегда пишут через let и const. var встречается в легаси-проектах, в старых ответах на Stack Overflow да в скриптах, которым нужно работать в совсем древних браузерах.

Hoisting и temporal dead zone

Все три способа объявления переменных подвержены всплытию (hoisting) — движок узнаёт о них ещё до того, как начинает выполнять код внутри блока, — но ведут они себя до строки с объявлением по-разному.

var всплывает и сразу получает значение undefined. Обратиться к такой переменной можно ещё до строки с var, и ошибки не будет:

index.js
Output
Click Run to see the output here.

let и const тоже проходят hoisting, только без инициализации. Если обратиться к ним до объявления — получите ошибку. Этот промежуток между входом в область видимости и выполнением самого объявления называют временной мёртвой зоной (temporal dead zone, TDZ):

index.js
Output
Click Run to see the output here.

TDZ — это не баг, а фича. Благодаря ей ситуация «использовал до объявления» превращается из тихого undefined в явную ошибку, и это ловит кучу опечаток и проблем с порядком кода.

Переменная цикла const в for...of

Небольшой, но частый случай: цикл for...of на каждой итерации создаёт новую привязку, поэтому для переменной цикла спокойно можно использовать const — хотя формально она «меняется» от итерации к итерации.

index.js
Output
Click Run to see the output here.

На каждой итерации создаётся своя привязка name — никакой единой переменной, которую бы переприсваивали, тут нет. А вот классический for (let i = 0; i < n; i++) по-прежнему требует let, потому что i — это одна привязка, которая инкрементируется.

Практическое правило

Выбирайте способ объявления переменных в таком порядке приоритета:

  • const — по умолчанию. Если значение не будет переприсвоено, так об этом и скажите.
  • let — когда привязке действительно нужно меняться.
  • var — только если работаете с кодовой базой, где это требуется.

Если придерживаться этого подхода последовательно, назначение каждой переменной становится понятно с одного взгляда: можно ли её переприсвоить, ограничена ли она блоком или функцией.

index.js
Output
Click Run to see the output here.

MAX_RETRIES и users не меняются — значит, const. successful растёт по ходу цикла — let. user на каждой итерации создаётся заново — снова const. Пробегая глазами сверху вниз, сразу понимаешь, какие значения двигаются, а какие прибиты гвоздями, — и для этого даже не нужно запускать код.

Дальше: примитивные типы

Раз уж вы умеете объявлять переменные, логичный следующий вопрос — какие значения в них вообще можно класть. В JavaScript есть небольшой набор примитивных типов: числа, строки, булевы значения и ещё пара штук — у каждого свои особенности. Об этом поговорим на следующей странице.

Часто задаваемые вопросы

В чём разница между let, const и var?

let и const появились в ES2015 и имеют блочную область видимости, а var — старый вариант с функциональной областью видимости. Значение, объявленное через const, нельзя переприсвоить, через let — можно. Кроме того, var поднимается (hoisting) и сразу инициализируется значением undefined, тогда как let и const тоже поднимаются, но обратиться к ним до строки объявления нельзя — это и есть temporal dead zone.

Делает ли const значение неизменяемым?

Нет. const запрещает только переприсваивание самой переменной. Если там лежит объект или массив, его содержимое спокойно меняется — код const user = {}; user.name = 'Ada' отработает без ошибок. Для настоящей неизменяемости используйте Object.freeze или специализированные библиотеки вроде Immer.

Стоит ли ещё использовать var в современном JavaScript?

Практически никогда. У let и const более предсказуемая область видимости, и многие ошибки отлавливаются ещё на этапе парсинга. var вам встретится разве что в старом легаси-коде или в скриптах, которым нужно работать в окружениях без поддержки ES2015.

Учитесь программировать с Coddy

НАЧАТЬ