JavaScript: область видимости простыми словами
Область видимости — важная концепция, определяющая доступность переменных. Данная концепция лежит в основе замыканий, разделяя переменные на глобальные и локальные.
В этой статье я постараюсь простыми словами объяснить, что такое область видимости в JavaScript.
1. Область видимости
Перед тем, как погружаться в детали, связанные с областью видимости, рассмотрим небольшой пример.
Допустим, мы определили переменную:
const message = 'Hello' console.log(message) // 'Hello'
Мы легко можем вывести ее значение в консоль. Это понятно.
Теперь поместим объявление переменной message в блок if:
if (true) < const message = 'Hello' >console.log(message) // ReferenceError: message is not defined
На этот раз при попытке доступа к переменной выбрасывается исключение ReferenceError: message is not defined.
Почему это произошло?
Потому что блок if создал область видимости для переменной message. И message доступна только внутри этой области.

Таким образом, доступность переменных ограничена областью видимости, в которой они определены.
Итак, область видимости — это зона доступности переменных.
2. Блочная область видимости
Блок кода в JavaScript определяет область видимости переменных, объявленных с помощью ключевых слов const и let:
if (true) < // область видимости блока if const message = 'Hello' console.log(message) // 'Hello' >console.log(message) // ReferenceError
Первый console.log() благополучно выводит значение переменной message в консоль, поскольку доступ к этой переменной осуществляется в той области видимости, в которой она определена.
Однако вызов второго console.log() приводит к возникновению ошибки, поскольку переменная message недоступна во внешней по отношению к ней области видимости: в текущем контексте message не существует.
В инструкциях if, for, while также создается блочная область видимости.
for (const color of ['green', 'red', 'blue']) < // область видимости блока for const message = 'Hi' console.log(color) // 'green', 'red', 'blue' console.log(message) // 'Hi', 'Hi', 'Hi' >console.log(color) // ReferenceError console.log(message) // ReferenceError
Переменные color и message существуют только внутри блока for.
Тоже самое справедливо для инструкции while:
while (/* условие */) < // область видимости блока while const message = 'Hi' console.log(message) // 'Hi' >console.log(message) // ReferenceError
message, определенная в while, доступна только внутри данного цикла.
В JavaScript вы можете создавать самостоятельные блоки кода. Они также определяют собственную область видимости:
< const message = 'Hi' console.log(message) // 'Hi' >console.log(message) // ReferenceError
2.1. var не имеет блочной области видимости
Как мы видели в предыдущих примерах, блок кода создает область видимости для переменных, объявленных с помощью ключевых слов const и let. Однако это не работает для переменных, объявленных с помощью ключевого слова var.
if (true) < // область видимости блока if var count = 0 console.log(count) // 0 >console.log(count) // 0
Переменная count, как и ожидалось, доступна внутри блока if. Однако, она доступна и за пределами данного блока!
Дело в том, что блок кода не создает области видимости для переменных, объявленных с помощью ключевого слова var. Но это делает функция.
3. Область видимости функции
Функции в JavaScript создают область видимости для всех переменных, независимо от того, с помощью какого ключевого слова они объявлены (var, const или let).
function run() < // область видимости функции run() var message = 'Беги, Форрест, беги!' console.log(message) >run() // 'Беги, Форрест, беги!' console.log(message) // ReferenceError
Функция run() создает область видимости. Переменная message доступна внутри функции, но недоступна снаружи.
Аналогичным образом функция создает область видимости для переменных, объявленных с помощью const и let, и даже для других функций и функциональных выражений:
function run() < // область видимости функции run() const two = 2 let one = 1 function run2() <>var run3 = () => <> console.log(two) console.log(one) console.log(run2) console.log(run3) > run() // 2 1 ƒ run2() <> () => <> console.log(two) // ReferenceError console.log(one) // ReferenceError console.log(run2) // ReferenceError console.log(run3) // ReferenceError
4. Область видимости модуля
Модули ES6 также создают область видимости для переменных, функций и классов.
Модуль circle создает константу pi (для внутреннего использования):
// область видимости модуля circle const pi = 3.14 console.log(pi) // 3.14 // использование pi
Переменная pi объявляется внутри модуля circle и не экспортируется из него.
Затем модуль circle импортируется:
import './circle' console.log(pi) // ReferenceError
Переменная pi недоступна за пределами модуля circle (до тех пор, пока она не будет экспортирована с помощью export).
Модульная область видимости инкапсулирует модули. Это означает, что частные переменные (которые не экспортируются) используются для собственных нужд модуля и защищены от доступа извне.
Таким образом, можно сказать, что область видимости — это механизм инкапсуляции для блоков кода, функций и модулей.
5. Области видимости могут быть вложенными
Интересной особенностью областей видимости является то, что они могут быть вложены одна в другую.
В следующем примере функция run() создает область видимости, а внутри нее блок if создает еще одну область:
function run() < // область видимости функции run() const message = 'Беги, Форрест, беги!' if (true) < // область видимости блока if const friend = 'Бубба' console.log(message) // 'Беги, Форрест, беги!' >console.log(friend) // ReferenceError > run()
Область видимости блока if вложена в область видимости функции run().
Область видимости, находящаяся внутри другой области, называется внутренней областью видимости. В приведенном примере — это область видимости блока if.
Область видимости, содержащая другую область, называется внешней областью видимости. В приведенном примере — это область видимости фукнции run().

Что насчет доступности переменных? Нужно запомнить простое правило:
Переменные из внешней области видимости доступны во внутренней области.
Поэтому переменная message доступна внутри блока if.
6. Глобальная область видимости
Глобальная область видимости является самой внешней областью. Она доступна для любой внутренней или локальной области видимости. В браузере глобальной является область видимости, создаваемая при загрузке JavaScript-файла, указанного в атрибуте src тега script:
// script.js
// глобальная область видимости
let counter = 1
Переменные, объявленные в глобальной области видимости являются глобальными переменными. Они доступны в любой другой области.
Глобальная область видимости — это механизм, который позволяет среде выполнения JavaScript-кода (браузеру, Node.js) предоставлять приложениям хостовые (т.е. принадлежащие среде) объекты как глобальные переменные.
Например, window и document являются глобальными переменными (объектами), предоставляемыми браузером. В Node.js такой переменной является, например, объект process.
7. Лексическая область видимости
Определим две функции, одна из которых вложена в другую:
function outer() < // область видимости функции outer() let v = 'Я из области видимости функции outer()!' function inner() < // область видимости функции inner() console.log(v) // 'Я из области видимости функции outer()!' >return inner > const f = outer() f()
Взгляните на последнюю строку: функция inner() вызывается за пределами области видимости функции outer(). Как JavaScript понимает, что значение, выводимое в консоль в функции inner(), принадлежит переменной v, объявленной в фукнции outer()?
Ответ: благодаря лексической области видимости.
JavaScript реализует механизм под названием лексическая или статическая область видимости. Лексическая область видимости означает, что доступность переменных определяется статически положением данных переменных внутри области видимости вложенной функции: переменные из области видимости внешней функции доступны в области видимости вложенной функции.
Формальное определение лексической области гласит следующее:
Лексическая область видимости состоит из внешних областей видимости, определенных статически, т.е. из внешних областей, зафиксированных использованием переменных из этих областей во внутренних функциях.
В приведенном примере лексическая область видимости функции inner() состоит из области видимости функции outer().
Более того, inner() — это замыкание, поскольку в ней используется значение переменной из лексической области видимости.
8. Изоляция переменных
Очевидно, что область видимости изолирует переменные. Благодаря этому разные области видимости могут содержать переменные с одинаковыми именами.
Вы можете использовать переменные count, index, current, value и т.д. в разных областях без угрозы возникновения коллизий (конфликтов имен).
function foo() < // область видимости функции foo() let count = 1 console.log(count) // 1 >function bar() < // область видимости функции bar() let count = 2 console.log(count) // 2 >foo() bar()
Заключение
Область видимости определяет доступность переменных. Переменная, объявленная в текущей области видимости, доступна только внутри нее.
В JavaScript области видимости создаются блоками, функциями и модулями.
Переменные, объявленные с помощью ключевых слова const и let могут иметь блочную, функциональную или модульную область видимости, а переменные, объявленные с помощью ключевого слова var, не имеют блочной области видимости.
Области видимости могут быть вложенными. Переменные, объявленные во внешней области видимости, доступны во внутренней области.
Лексическая область видимости состоит из внешних областей, определенных статически. Любая функция, независимо от места выполнения, имеет доступ к переменным из ее лексической области видимости (в этом заключается суть замыканий).
Надеюсь статья была вам полезной. Благодарю за внимание.
- javascript
- программирование
- разработка
- scope
- область видимости
- Веб-разработка
- JavaScript
- Программирование
Переменные const , let и var
Чем отличаются const , let или var ? Рассказываем, что такое переменные и какими они бывают.
Время чтения: 9 мин
Открыть/закрыть навигацию по статье
- Кратко
- Что такое переменные
- Правила именования переменных
- Создание переменных
- Переменные let и const
- Объявление
- Одинаковые имена переменных
- Смена значения в let и const
- Объявление
- Смена значения в var
- Сергей Фомин советует
- В чём разница между null, undefined и объявленной переменной без начального значения? (let foo;)
- Разница между let, const и var?
Контрибьюторы:
Обновлено 14 ноября 2022
Кратко
Скопировать ссылку «Кратко» Скопировано
Переменные в JavaScript хранят значения, которыми оперирует код. Для создания переменных используются ключевые слова var , let и const .
Использование var оправданно, если нужно писать код для старых браузеров времён IE 11 или Opera mini. Во всех остальных случаях лучше использовать let и const , так как они не позволят допустить ошибки, приводящие к неправильным значениям в переменных или изменениям глобальных переменных.
Что такое переменные
Скопировать ссылку «Что такое переменные» Скопировано
Переменные — это именованные контейнеры для хранения данных.
Для создания переменной используется ключевое слово let , const или var . Сразу за ключевым словом идёт название переменной либо перечень переменных через запятую. Создание переменной также называют объявлением переменной. Например:
var singleVariable let firstVariable, secondVariable, thirdVariablevar singleVariable let firstVariable, secondVariable, thirdVariableЧаще всего, при объявлении переменной ей устанавливают стартовое значение при помощи оператора присваивания = . Тип значения может быть абсолютно любым — строка, число, объект, массив и так далее.
// Объявление переменной и присваиваниеlet string = 'foo'const array = ['foo', 'bar', 'baz']var number = 10 // Множественное объявление и присваиваниеlet firstNumber = 5, secondNumber = 10// Объявление переменной и присваивание let string = 'foo' const array = ['foo', 'bar', 'baz'] var number = 10 // Множественное объявление и присваивание let firstNumber = 5, secondNumber = 10Затем переменную можно использовать как заменитель значения в коде:
let name = 'Уолтер'let surname = 'Вайт'let fullName = name + ' ' + surname console.log(fullName)// Уолтер Вайтlet name = 'Уолтер' let surname = 'Вайт' let fullName = name + ' ' + surname console.log(fullName) // Уолтер ВайтПравила именования переменных
Скопировать ссылку «Правила именования переменных» Скопировано
Для имени переменной можно использовать следующие символы:
- буквы латинского алфавита;
- цифры;
- символы $ и _ .
Первый символ не должен быть цифрой:
let letters, $dollarsign, _underscore let 1number// SyntaxError: Invalid or unexpected tokenlet letters, $dollarsign, _underscore let 1number // SyntaxError: Invalid or unexpected tokenВ качестве названий переменных нельзя использовать зарезервированные языком слова. Например: class , super , throw , yield , var , let , const и так далее. С полным списком таких слов можно ознакомиться здесь.
Создание переменных
Скопировать ссылку «Создание переменных» Скопировано
Перед выполнением скрипта JavaScript находит код создания переменных и заранее создаёт их. Получается, что в начале выполнения скрипта все переменные, описанные в коде, уже объявлены. В зависимости от браузера, они могут быть равны undefined (в Chrome и Safari), либо, в случае с let и const в браузере Firefox, не равны ничему и иметь специальное состояние uninitialized :
console.log('Старт') var byVar = 5let byLet = 10const byConst = 15 console.log('Конец')console.log('Старт') var byVar = 5 let byLet = 10 const byConst = 15 console.log('Конец')
В конце скрипта, после того как произошло присвоение стартовых значений, переменные равны 5 , 10 и 15 :

Получается, что некоторое время переменная может содержать значение undefined и быть доступной для чтения. Этим нужно пользоваться с осторожностью.
Переменные let и const
Скопировать ссылку «Переменные let и const» Скопировано
Переменные let и const появились в версии EcmaScript 2015 года (ES6), и сейчас используются намного чаще чем var .
Объявление
Скопировать ссылку «Объявление» Скопировано
Используя ключевое слово let , можно объявить переменную без присвоения ей начального значения. В таком случае она будет равна undefined :
let aconsole.log(a)// undefined a = 5console.log(a)// 5let a console.log(a) // undefined a = 5 console.log(a) // 5При помощи const нельзя объявлять переменные без значения:
const a// SyntaxError: Missing initializer in const declaration // Правильноconst b = 5const a // SyntaxError: Missing initializer in const declaration // Правильно const b = 5К переменным let и const нельзя обращаться до их объявления в коде:
console.log(a)// ReferenceError: Cannot access 'a' before initializationconsole.log(b)// ReferenceError: Cannot access 'b' before initialization let a = 5const a = 5console.log(a) // ReferenceError: Cannot access 'a' before initialization console.log(b) // ReferenceError: Cannot access 'b' before initialization let a = 5 const a = 5Почему так?
У let и const есть так называемая temporal dead zone (TDZ) — момент выполнения скрипта до объявления переменной. Переменная может использоваться и выше объявления, при условии, что содержащая её часть кода будет выполнена после инициализации:
function foo() console.log('from foo', a)> Promise.resolve() .then(() => console.log('from promise', a)) setTimeout(() => console.log('from timer',a)) let a = 10 foo() // 'from foo 10', 'from promise 10', 'from timer 10'function foo() console.log('from foo', a) > Promise.resolve() .then(() => console.log('from promise', a)) setTimeout(() => console.log('from timer',a)) let a = 10 foo() // 'from foo 10', 'from promise 10', 'from timer 10'TDZ есть также и у ES6-классов, несмотря на то, что они являются «синтаксическим сахаром» над обычными функциями.
console.log(Foo) class Foo constructor(bar) this.bar = bar >>// ReferenceError: Cannot access 'Foo' before initializationconsole.log(Foo) class Foo constructor(bar) this.bar = bar > > // ReferenceError: Cannot access 'Foo' before initializationА функции (объявленные как Function Declaration) TDZ не имеют.
console.log(Foo) function Foo() this.bar = bar>// ƒ Foo()console.log(Foo) function Foo() this.bar = bar > // ƒ Foo()Оба типа переменных имеют блочную область видимости и не становятся частью глобального объекта ( window в браузере, global в Node.js). Блочная область видимости не даёт получить значение переменной вне блока, где она была объявлена.
Если объявить переменные внутри блока if , то обращение к ним вне блока будет выбрасывать ошибку:
if (true) let a = 5 const b = 10 console.log(a) // 5 console.log(b) // 10> console.log(a)// ReferenceError: a is not defined console.log(b)// ReferenceError: b is not definedif (true) let a = 5 const b = 10 console.log(a) // 5 console.log(b) // 10 > console.log(a) // ReferenceError: a is not defined console.log(b) // ReferenceError: b is not definedОдинаковые имена переменных
Скопировать ссылку «Одинаковые имена переменных» Скопировано
Объявление переменной с именем, которое уже используется в текущей области видимости, приведёт к ошибке:
let a = 5 let a = 10// SyntaxError: Identifier 'a' has already been declaredlet a = 5 let a = 10 // SyntaxError: Identifier 'a' has already been declaredТо же правило работает и при использовании const , и при использовании смешанного подхода:
const a = 5const a = 10// SyntaxError: Identifier 'a' has already been declared var b = 5const b = 10// SyntaxError: Identifier 'b' has already been declaredconst a = 5 const a = 10 // SyntaxError: Identifier 'a' has already been declared var b = 5 const b = 10 // SyntaxError: Identifier 'b' has already been declaredВ то же время можно объявлять переменные с одинаковым именем в разных областях видимости. В этом случае значение будет зависеть от той области видимости, где происходит чтение:
let name = 'Ольга' if (true) let name = 'Елена' console.log(name) // Елена> console.log(name)// Ольгаlet name = 'Ольга' if (true) let name = 'Елена' console.log(name) // Елена > console.log(name) // ОльгаСмена значения в let и const
Скопировать ссылку «Смена значения в let и const» Скопировано
Значение в переменной, созданной через let , можно изменять:
let a = 5console.log(a)// 5 a = 10console.log(a)// 10let a = 5 console.log(a) // 5 a = 10 console.log(a) // 10Стартовое значение const изменить нельзя, будь то примитивное значение:
const a = 5a = 10// TypeError: Assignment to constant variableconst a = 5 a = 10 // TypeError: Assignment to constant variableИли ссылка на объект:
const obj = a: 5,> obj = a: 10,>// TypeError: Assignment to constant variableconst obj = a: 5, > obj = a: 10, > // TypeError: Assignment to constant variableОднако объект, хранящийся в const , можно мутировать. Объекты хранятся по ссылке, и изменение объекта не приводит к изменению ссылки на него:
const obj = a: 5,> obj.a = 10 console.log(obj)//const obj = a: 5, > obj.a = 10 console.log(obj) //Переменные var
Скопировать ссылку «Переменные var» Скопировано
Объявление переменных при помощи ключевого слова var было в JavaScript с первых версий.
Объявление
Скопировать ссылку «Объявление» Скопировано
Переменные var можно объявлять без присвоения им значения, в таком случае они будут равны undefined :
var aconsole.log(a)// undefined var b = 5console.log(b)// 5var a console.log(a) // undefined var b = 5 console.log(b) // 5Переменные, объявленные через var , имеют функциональную область видимости. Они доступны только в пределах текущей функции или глобального объекта, если функции нет:
if (true) var a = 5> function foo() var b = 10> console.log(a)// 5console.log(b)// ReferenceError: b is not definedif (true) var a = 5 > function foo() var b = 10 > console.log(a) // 5 console.log(b) // ReferenceError: b is not definedОбъявление переменных вне функций делает их глобальными переменными. Они доступны как свойства глобального объекта:
var varVariable = 5 console.log(window.varVariable)// 5var varVariable = 5 console.log(window.varVariable) // 5К переменным, объявленным при помощи ключевого слова var , можно обращаться до момента объявления. В отличие от let и const , ошибки это не вызовет. Такое поведение называется hoisting — «всплытие»:
console.log(a)// undefined var a = 5 console.log(a)// 5console.log(a) // undefined var a = 5 console.log(a) // 5Разберём, как работает функциональная область видимости:
var a = 5 function foo() console.log(a) // undefined var a = 10 console.log(a) // 10> foo() console.log(a)// 5var a = 5 function foo() console.log(a) // undefined var a = 10 console.log(a) // 10 > foo() console.log(a) // 5Перед выполнением функции в глобальной области видимости присутствует переменная a , равная 5 :

Во время выполнения функции формируется новая функциональная область видимости, в которой тоже присутствует переменная a . Эта переменная была объявлена с помощью var внутри функции, в момент выполнения которой она «всплыла» со значением равным undefined . В строке 4 происходит обращение именно к ней (до её объявления), а не к той, что находится вне функции.

В строке 8 значение переменной a уже равно 10.

После выполнения функции локальная область видимости была удалена. В консоли выводится глобальная переменная a .

Более подробно об этом можно прочитать в отдельной статье
Смена значения в var
Скопировать ссылку «Смена значения в var» Скопировано
Значение, хранящееся в переменной var , можно изменить двумя способами:
- обратиться к имени переменной и присвоить новое значение:
var a = 5console.log(a)// 5 a = 10console.log(a)// 10var a = 5 console.log(a) // 5 a = 10 console.log(a) // 10- обратиться к имени переменной вместе с ключевым словом var :
var a = 5console.log(a)// 5 var a = 10console.log(a)// 10var a = 5 console.log(a) // 5 var a = 10 console.log(a) // 10На практике
Скопировать ссылку «На практике» Скопировано
Сергей Фомин советует
Скопировать ссылку «Сергей Фомин советует» Скопировано
В новом коде используйте только let или const . Используйте let в тех случаях, когда значение переменной меняется. Во всех остальных используйте const . Проще всего всегда по умолчанию использовать ключевое слово const и исправлять объявление переменной на let , если появляется нужда изменить её значение далее в коде.
Называйте переменные так, чтобы можно было легко понять, что в них хранится. Например:
let url = 'https://doka.guide' const now = Date.now() const user = name: 'John', age: 30,>let url = 'https://doka.guide' const now = Date.now() const user = name: 'John', age: 30, >Исключением считается именование счётчиков в циклах for , в которых обычно используются одиночные буквы i , j , и так далее.
Имена переменных могут состоять из нескольких слов, поэтому для удобства их чтения в JavaScript принято использовать так называемую «верблюжью нотацию» (camelCase), когда каждое новое слово, начиная со второго, пишется с заглавной буквы:
const fullName = 'John Doe' const arrayOfNumbers = [1, 2, 3]const fullName = 'John Doe' const arrayOfNumbers = [1, 2, 3]Имена констант (переменные, которые не меняют своё значение) принято писать, используя screaming_snake_case. В данной нотации все слова пишутся заглавными буквами, а разделителем является символ _ .
const BASE_URL = 'https://doka.guide' const PORT = 3000 const UNAUTHORIZED_CODE = 401const BASE_URL = 'https://doka.guide' const PORT = 3000 const UNAUTHORIZED_CODE = 401+ Развернуть
На собеседовании
Скопировать ссылку «На собеседовании» Скопировано
В чём разница между null , undefined и объявленной переменной без начального значения? ( let foo; )
Скопировать ссылку «В чём разница между null, undefined и объявленной переменной без начального значения? (let foo;)» Скопировано
Скопировать ссылку «Александр Рассудихин отвечает» Скопировано
null задаётся переменной явно и означает, что она является объектом, но структура этого объекта ещё не определена. undefined присваивается переменной (переменная не декларирует объект), когда она была объявлена, но не было определено её начальное значение. Функция может возвращать undefined или null . Всё зависит от того, что мы ожидаем в результате работы функции. Если мы ожидаем объект, но по каким-то причинам функция его вернуть не может, то возвращаем null . Если функция должна вернуть, например, число (главное, не объект), но не может этого сделать, то она возвращает undefined .
Без начального значения можно оставлять только переменную, объявленную через let или var . Если объявить переменную через const и не задать ей начального значения, будет ошибка: Uncaught SyntaxError : Missing initializer in const declaration .
Поговорим немного о приведении типов. Для начала, пример:
console.log(null + null); // 0console.log(undefined + undefined); // NaNconsole.log(null + null); // 0 console.log(undefined + undefined); // NaN- null во время сложения приводится к нулю;
- undefined во время сложения приводится к NaN . NaN это аббревиатура от «not a number» — не число. Результат арифметической операции равен NaN , если во время операции произошла ошибка и ожидаемый числовой результат не может быть выражен числом.
Есть ещё один хитрый пример:
console.log(null + []); // "null"console.log(null + []); // "null"Подсказка, почему так, кроется именно в типе результате: «null» — строка. А не примитивное значение null .
JavaScript сначала приводит массив к примитивному значению. Для этого вызывается метод to String ( ) , который вызывает метод join ( ) . Т.к. массив пустой, то join ( ) вернёт пустую строку «» . А сложение чего-то со строкой в JS возвращает строку. Поэтому null уже никуда не приводится, а возращается строка «null» .
Немного упомяну и про оператор нулевого слияния ( ? ? ). В выражении между двумя операндами он будет возвращать первый операнд, если он не равен null или undefined . Можно сказать, что ? ? приравнивает смысл undefined и null к «ничего не содержит», и, в этом случае, кладёт в переменную значение второго операнда.
Разница между let , const и var ?
Скопировать ссылку «Разница между let, const и var?» Скопировано
Скопировать ссылку «Алексей Филимонов отвечает» Скопировано
Переменные объявленные через var всплывают (hoisting).
Это значит, что если мы обратимся к переменной ещё до момента её инициализации, то получим undefined .console.log(a); // Не может получить доступ к 'a' до её инициализацииconsole.log(b); // Не может получить доступ к 'b' до её инициализацииconsole.log(c); // undefined let a = 10;const b = 20;var c = 30;console.log(a); // Не может получить доступ к 'a' до её инициализации console.log(b); // Не может получить доступ к 'b' до её инициализации console.log(c); // undefined let a = 10; const b = 20; var c = 30;У переменных let , const и var разная область видимости.
У let и const область видимости ограничена блоком, а не функцией.
Другими словами, если переменные let и const объявлены внутри < . . . >, то доступны только там и на всех вложенных уровнях. Переменная, объявленная через var такую область видимости игнорирует и может быть доступна за её пределами.if (true) let a = 10; const b = 20; var c = 30;> console.log(a); // ReferenceErrorconsole.log(b); // ReferenceErrorconsole.log(c); // 30if (true) let a = 10; const b = 20; var c = 30; > console.log(a); // ReferenceError console.log(b); // ReferenceError console.log(c); // 30Переменная, объявленная через const становиться константой и её невозможно переопределить. При попытке это сделать мы получим ошибку.
let a = 10;const b = 20; a = 15 // Все впорядкеb = 40 // TypeErrorlet a = 10; const b = 20; a = 15 // Все впорядке b = 40 // TypeErrorVar js что это
Оператор var объявляет переменную, инициализируя её, при необходимости.
Интерактивный пример
Синтаксис
var varname1 [= value1 [, varname2 [, varname3 . [, varnameN]]]];
Имя переменной. Может использоваться любой допустимый идентификатор.
Значение переменной. Любое допустимое выражение. По умолчанию значение undefined.
Описание
Объявление переменной всегда обрабатывается до выполнения кода, где бы она ни находилась. Область видимости переменной, объявленной через var , это её текущий контекст выполнения. Который может ограничиваться функцией или быть глобальным, для переменных, объявленных за пределами функции.
Присвоение значения необъявленной переменной подразумевает, что она будет создана как глобальная переменная (переменная становится свойством глобального объекта) после выполнения присваивания значения. Различия между объявленной и необъявленной переменными следующие:
1. Объявленные переменные ограничены контекстом выполнения, в котором они были объявлены. Необъявленные переменные всегда глобальны.
function x() y = 1; // возбудит ReferenceError в "строгом режиме" var z = 2; > x(); console.log(y); // выведет "1" console.log(z); // возбудит ReferenceError: z не определён вне x
2. Объявленные переменные инициализируются до выполнения любого кода. Необъявленные переменные не существуют до тех пор, пока к ним не выполнено присваивание.
.log(a); // Возбудит ReferenceError. console.log("still going. "); // Не выполнится.
var a; console.log(a); // Выведет "undefined" или "", в зависимости от браузера. console.log("still going. "); // Выведет "still going. ".
3. Объявленные переменные, независимо от контекста выполнения, являются ненастраиваемыми свойствами. Необъявленные переменные это настраиваемые свойства (т.е. их можно удалять).
var a = 1; b = 2; delete this.a; // Возбудит TypeError в "строгом режиме". В "нестрогом режиме" будет ошибка без уведомления. delete this.b; console.log(a, b); // Возбудит ReferenceError. // Свойство 'b' было удалено и больше не существует.
Из-за перечисленных различий, использование необъявленных переменных может привести к непредсказуемым последствиям. Рекомендовано всегда объявлять переменные, вне зависимости, находятся они внутри функции или в глобальном контексте. Присваивание значения необъявленной переменной в строгом режиме (en-US) ECMAScript 5 возбуждает ошибку.
Поднятие переменных
Объявление переменных (как и любые другие объявления) обрабатываются до выполнения кода. Где бы не находилось объявление, это равнозначно тому, что переменную объявили в самом начале кода. Это значит, что переменная становится доступной до того, как она объявлена. Такое поведение называется «поднятием» (в некоторых источниках «всплытием»).
= 2; var bla; // . // читается как: var bla; bla = 2;
Поэтому объявление переменных рекомендовано выносить в начало их области видимости (в начало глобального кода или в начало функции). Это даёт понять какие переменные принадлежат функции (т.е. являются локальными), а какие обрабатываются в цепи областей видимости (т.е. являются глобальными).
Важно отметить, что подъем будет влиять на объявление переменной, но не на инициализацию её значения. Значение присваивается при выполнении оператора присваивания:
function do_something() console.log(bar); // выведет undefined var bar = 111; console.log(bar); // выведет 111 > // . неявно понимается как: function do_something() var bar; console.log(bar); // выведет undefined bar = 111; console.log(bar); // выведет 111 >
Устаревшее ключевое слово «var»
Информация, приведенная в этой статье, полезна для понимания старых скриптов.
Мы не пишем современный код таким образом.
В самой первой главе про переменные мы ознакомились с тремя способами объявления переменных:
let и const ведут себя одинаково по отношению к лексическому окружению, области видимости.
Но var – это совершенно другой зверь, берущий своё начало с давних времён. Обычно var не используется в современных скриптах, но всё ещё может скрываться в старых.
Если в данный момент вы не работаете с подобными скриптами, вы можете пропустить или отложить прочтение данной главы, однако, есть шанс, что вы столкнётесь с var в будущем.
На первый взгляд, поведение var похоже на let . Например, объявление переменной:
function sayHi() < var phrase = "Привет"; // локальная переменная, "var" вместо "let" alert(phrase); // Привет >sayHi(); alert(phrase); // Ошибка: phrase не определена…Однако, отличия всё же есть.
Для «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); // Error: test is not definedАналогично для циклов: var не может быть блочной или локальной внутри цикла:
for (var i = 0; i < 10; i++) < // . >alert(i); // 10, переменная i доступна вне цикла, т.к. является глобальной переменнойЕсли блок кода находится внутри функции, то var становится локальной переменной в этой функции:
function sayHi() < if (true) < var phrase = "Привет"; >alert(phrase); // срабатывает и выводит "Привет" > sayHi(); alert(phrase); // Ошибка: phrase не определена (видна в консоли разработчика)Как мы видим, var выходит за пределы блоков if , for и подобных. Это происходит потому, что на заре развития JavaScript блоки кода не имели лексического окружения. Поэтому можно сказать, что var – это пережиток прошлого.
«var» допускает повторное объявление
Если в блоке кода дважды объявить одну и ту же переменную let , будет ошибка:
let user; let user; // SyntaxError: 'user' has already been declaredИспользуя var , можно переобъявлять переменную сколько угодно раз. Повторные var игнорируются:
var user = "Pete"; var user; // ничего не делает, переменная объявлена раньше // . нет ошибки alert(user); // PeteЕсли дополнительно присвоить значение, то переменная примет новое значение:
var user = "Pete"; var user = "John"; alert(user); // John«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();Это поведение называется «hoisting» (всплытие, поднятие), потому что все объявления переменных var «всплывают» в самый верх функции.
В примере выше if (false) условие никогда не выполнится. Но это никаким образом не препятствует созданию переменной var phrase , которая находится внутри него, поскольку объявления 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 = "Hello"; alert(message); // Hello >)();Здесь создаётся и немедленно вызывается Function Expression. Так что код выполняется сразу же и у него есть свои локальные переменные.
Function Expression обёрнуто в скобки (function <. >) , потому что, когда JavaScript встречает «function» в основном потоке кода, он воспринимает это как начало Function Declaration. Но у Function Declaration должно быть имя, так что такой код вызовет ошибку:
// Пробуем объявить и сразу же вызвать функцию function() < // ();Даже если мы скажем: «хорошо, давайте добавим имя», – это не сработает, потому что JavaScript не позволяет вызывать Function Declaration немедленно.
// ошибка синтаксиса из-за скобок ниже function go() < >(); //Так что скобки вокруг функции – это трюк, который позволяет объяснить JavaScript, что функция была создана в контексте другого выражения, а значит, что это Function Expression: ей не нужно имя и её можно вызвать немедленно.
Помимо круглых скобок существуют и другие способы сообщить JavaScript, что мы имеем в виду Function Expression:
// Способы создания IIFE (function() < alert("Круглые скобки вокруг функции"); >)(); (function() < alert("Круглые скобки вокруг всего выражения"); >()); !function() < alert("Выражение начинается с логического оператора НЕ"); >(); +function() < alert("Выражение начинается с унарного плюса"); >();Во всех перечисленных случаях мы объявляем Function Expression и немедленно запускаем его. Ещё раз отметим: в настоящее время необходимости писать подобный код нет.
Итого
Существует 2 основных отличия var от let/const :
- Переменные var не имеют блочной области видимости, они ограничены, как минимум, телом функции.
- Объявления (инициализация) переменных var производится в начале исполнения функции (или скрипта для глобальных переменных).
Есть ещё одно небольшое отличие, относящееся к глобальному объекту, мы рассмотрим его в следующей главе.
Эти особенности, как правило, не очень хорошо влияют на код. Блочная область видимости – это удобно. Поэтому много лет назад let и const были введены в стандарт и сейчас являются основным способом объявления переменных.