October 23, 2018

Видимость var, let, const. Всплытие переменных

var

Видна внутри функции, в которой была объявлена

function foo () {
   for (var i = 0; i < 3; i++) {
      console.log(i) // 0, 1, 2
   }
   console.log(i) // 3
}

После выполнения в цикле переменная i все еще видна внутри функции, поэтому примет значение 3.

Обладает свойством всплытия/поднятия

function foo () {
   var bar = 1
   console.log(bar) // 1
   var bar = 2
   console.log(bar) // 2
}

При выполнении кода объявление переменной bar всплывет в начало функции, то есть код будет эквивалентен следующему

function foo () {
   var bar
   bar = 1
   console.log(bar) // 1
   bar = 2
   console.log(bar) // 2
}

Как видно, всплывает только объявление переменной, присвоение значения - остается на прежнем месте.

А значит и следующий код, который выглядит слегка абсурдно, выполнится без ошибок:

function foo () {
   if (true) bar = 2
   return bar
   var bar = 1
}

Объявление переменной bar всплывет в начало функции, поэтому из-за return будет проигнорировано только последнее присваивание значения, но функция все равно вернет значение 2.

Записывается в свойство window/global при объявлении вне функции

var bar = 1
function foo () {
   var bar = 2
   console.log(bar) // 2
   console.log(window.bar) // 1
}
console.log(bar) // 1

Внешняя переменная не будет перезаписана при объявлении внутренней переменной. К внешней переменной можно обратиться через window.

let

Видна только внутри блока { ... }

let foo = 1
if (true) {
  let foo = 2
  console.log(foo) // 2
}
console.log(foo) // 1

Так как переменные, объявленные с помощью let, видны только внутри логического блока, то в данном коде не будет никаких пересечений или переопределений.

Видна только после объявления и не может быть переопределена

function foo () {
   if (true) bar = 2 // ReferenceError: bar is not defined
   return bar
   let bar = 1
}

В данном случае получим ошибку, так как в отличие от var, объявление переменной bar через let не всплывает в начало функции.

function foo () {
   let bar = 1
   console.log(bar)
   let bar = 2      // SyntaxError: Identifier 'bar' has already been declared
   console.log(bar)
}

В таком случае тоже получим ошибку, так как в пределах блока { ... } уже была определена переменная bar.

const

Отличается от let только тем, что значение константы не может быть перезаписано.

let foo = 1
foo = 2
console.log(foo) // 2

const bar = 1
bar = 2          // TypeError: Assignment to constant variable
console.log(bar)

Однако, если константой является объект, то его свойства по прежнему могут изменяться.

const obj = { foo: 1 }
obj.foo = 2
console.log(obj) // { foo: 2 }