Ключевое слово var в JavaScript
Как работает var в JavaScript: область видимости, поднятие, глобальные переменные, повторное объявление, баг замыканий в цикле, и почему let и const пришли на смену.
В JavaScript то, как вы создаёте и используете переменные, напрямую влияет на поведение кода. Ключевое слово var десятилетиями применялось для объявления переменных. После того как в ES6 появились let и const, var редко является правильным выбором в новом коде — однако вы будете продолжать встречать его в учебниках, на собеседованиях и в устаревших проектах. В этой главе подробно объясняется, как работает var, почему его правила области видимости и поднятия удивляют разработчиков, и когда (если вообще) стоит его использовать сегодня.
На этой странице рассматривается: область видимости var, поднятие и ловушка undefined, случайные глобальные переменные, повторное объявление, знаменитый баг замыканий в цикле, а также более безопасные альтернативы — let/const.
Введение в объявление переменных с помощью var
Ключевое слово var объявляет переменную, опционально инициализируя её значением. Переменные, объявленные через var, имеют функциональную область видимости или глобальную область видимости (но никогда не блочную) и подвергаются поднятию (hoisting), что может приводить к поведению, которое удивляет разработчиков, пришедших из других языков. Основы объявления переменных описаны в разделе Переменные JavaScript.
Функциональная область видимости
Переменная, объявленная с помощью var внутри функции, является локальной для этой функции и доступна в любом месте внутри неё. Принципиально важно: var не соблюдает границы блоков. Даже если переменная объявлена внутри блока — например, в операторе if или цикле for — она «вытекает» наружу и остаётся видимой во всей охватывающей функции.
Это главное отличие от let и const: с ними x существовал бы только внутри блока if, а console.log выбросил бы ReferenceError. Полную картину блочной области видимости см. в разделе Область видимости переменных.
Поднятие (Hoisting)
Поднятие — это поведение JavaScript, при котором объявления обрабатываются так, будто они перемещены в начало своей области видимости. Для var поднимается только объявление, но не присваивание — поэтому имя переменной существует с начала функции, но хранит undefined до тех пор, пока выполнение не достигнет строки с присваиванием.
Чтение y до строки с var даёт undefined, а не ReferenceError. Именно в этом тонкость поднятия var: код выглядит так, будто «работает», но опечатка или неверно размещённое присваивание могут незаметно прочитать undefined.
let и const тоже поднимаются, но они находятся в временной мёртвой зоне (temporal dead zone) до момента выполнения их объявления, поэтому обращение к ним раньше выбрасывает исключение вместо того, чтобы молча вернуть undefined — обычно именно это и нужно:
Глобальные переменные и ключевое слово var
Когда var используется вне какой-либо функции, переменная становится глобальной и доступна откуда угодно. В браузерах она также прикрепляется как свойство глобального объекта (window), чего let и const не делают. Риск в том, что два скрипта, каждый из которых объявляет var config, незаметно перезапишут друг друга.
var globalVar = "I am global";
// In a browser: window.globalVar === "I am global" → true
let blockGlobal = "scoped";
// window.blockGlobal → undefined (let/const do not become window properties)Забывание объявить переменную вовсе (присваивание необъявленному имени в режиме без строгого режима) также создаёт случайную глобальную переменную — ещё одна причина использовать "use strict" и предпочитать let/const.
Ограничения var и современные альтернативы
Несмотря на то что var является фундаментальной частью JavaScript, он несёт в себе ограничения, которые привели к появлению let и const в ES6 (ECMAScript 2015), предоставляющих переменные с блочной областью видимости.
Путаница с поднятием и областью видимости
Поведение при поднятии и функциональная природа области видимости var могут порождать путаницу, особенно в конструкциях с циклами или условными блоками, где разработчики интуитивно ожидают блочной видимости переменных.
Повторное объявление var и особенности замыканий в цикле
- Повторное объявление: В отличие от
letиconst,varпозволяет повторно объявить одну и ту же переменную в той же области видимости без ошибки — поэтому дублирующее объявление может незаметно перезаписать предыдущее. - Замыкание в цикле: Переменная цикла, объявленная через
var, находится в одной общей функциональной области видимости, поэтому каждое замыкание, созданное в цикле, видит одну и ту же переменную. К моменту выполнения колбэков цикл завершается, и переменная хранит своё конечное значение.
Баг замыкания в цикле — классическая ловушка var. Все три колбэка делят одну переменную i:
Замена var на let решает проблему: let создаёт новую привязку для каждой итерации, поэтому каждое замыкание захватывает собственное значение:
До появления let обходным решением было использование IIFE, захватывающего значение на каждой итерации:
Введение let и const
Чтобы устранить проблемы, связанные с var, ключевые слова let и const предоставляют разработчикам переменные с блочной областью видимости, снижая риск ошибок, вызванных поднятием, и делая код более предсказуемым и удобным для отладки.
Рекомендации по использованию var в JavaScript
Несмотря на свои ограничения, var остаётся частью языка JavaScript и может встречаться, особенно в устаревшем коде. Соблюдение рекомендаций гарантирует, что его использование не навредит функциональности или поддерживаемости кода.
- Ограничьте использование в современном коде: Предпочитайте
letиconstдля объявления переменных, чтобы воспользоваться преимуществами блочной области видимости и снизить потенциальные проблемы с поднятием. - Понимайте область видимости: При работе с
varучитывайте его функциональную область видимости и планируйте структуру кода соответствующим образом, чтобы избежать непреднамеренных глобальных переменных. - Инициализация при объявлении: Инициализируйте переменные
varв момент объявления, чтобы избежать значенийundefinedиз-за поднятия.
Заключение
Ключевое слово var долгое время было краеугольным камнем JavaScript. Понимание его области видимости и поведения при поднятии необходимо для написания надёжного кода. По мере развития языка были введены let и const, устраняющие ограничения var и делающие современный JavaScript более предсказуемым. Тем не менее понимание var остаётся важным для поддержки устаревших кодовых баз. Следуя передовым практикам и отдавая предпочтение объявлениям с блочной областью видимости, разработчики могут писать более чистый и поддерживаемый JavaScript.