Стоят две банки: прозрачная с лимонным джемом и непрозрачная с  мёдом. На заднем плане открытое окошко операционной системы с пчёлкой
Иллюстрация: kirakusto

Переменные const, let и var

Чем отличаются const, let или var? Рассказываем, что такое переменные и какими они бывают

Время чтения: 9 мин

Кратко

Секция статьи "Кратко"

Переменные в JavaScript хранят значения, которыми оперирует код. Для создания переменных используются ключевые слова var, let и const.

Что такое переменные

Секция статьи "Что такое переменные"

Переменные — это именованные контейнеры для хранения данных.

Для создания переменной используется ключевое слово let, const или var. Сразу за ключевым словом идёт название переменной либо перечень переменных через запятую. Создание переменной также называют объявлением переменной. Например:

        
          
          var singleVariablelet firstVariable, secondVariable, thirdVariable
          var 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 + ' ' + surnameconsole.log(fullName)// Уолтер Вайт
          let name = 'Уолтер'
let surname = 'Вайт'
let fullName = name + ' ' + surname

console.log(fullName)
// Уолтер Вайт

        
        
          
        
      

Правила именования переменных

Секция статьи "Правила именования переменных"

Для имени переменной можно использовать следующие символы:

  • буквы латинского алфавита;
  • цифры;
  • символы $ и _.

Первый символ не должен быть цифрой:

        
          
          let letters, &ampersand, _underscorelet 1number// SyntaxError: Invalid or unexpected token
          let letters, &ampersand, _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 = 15console.log('Конец')
          console.log('Старт')

var byVar = 5
let byLet = 10
const byConst = 15

console.log('Конец')

        
        
          
        
      
Дебаггер Firefox, выполнение только началось, и все переменные уже объявлены, хотя код ещё не дошёл до выполнения нужных строк.
Код ещё не начал выполняться, а переменные уже объявлены.

В конце скрипта, после того как произошло присвоение стартовых значений, переменные равны 5, 10 и 15:

Дебаггер Firefox, выполнение дошло до строк с объявлением переменных и значения установлены.
JavaScript выполнил код объявления переменных и установил значения.

Получается, что некоторое время переменная может содержать значение undefined и быть доступной для чтения. Этим нужно пользоваться с осторожностью.

Переменные let и const

Секция статьи "Переменные let и const"

Переменные let и const появились в версии EcmaScript 2015 года (ES6), пришли на смену устаревшему var.

Объявление

Секция статьи "Объявление"

Используя ключевое слово let, можно объявить переменную без присвоения ей начального значения. В таком случае она будет равна undefined:

        
          
          let aconsole.log(a)// undefineda = 5console.log(a)// 5
          let a
console.log(a)
// undefined

a = 5
console.log(a)
// 5

        
        
          
        
      

При помощи const нельзя объявлять переменные без значения:

        
          
          const a// SyntaxError: Missing initializer in const declaration// Правильноconst b = 5
          const 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 initializationlet a = 5const a = 5
          console.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 = 10foo()// '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 initialization
          console.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() { this.bar = bar}
          console.log(Foo)

function Foo() {
  this.bar = bar
}
// ƒ Foo() { this.bar = bar}

        
        
          
        
      

Оба типа переменных имеют блочную область видимости и не становятся частью глобального объекта (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 definedconsole.log(b)// ReferenceError: b is not defined
          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 defined

        
        
          
        
      

Одинаковые имена переменных

Секция статьи "Одинаковые имена переменных"

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

        
          
          let a = 5let a = 10// SyntaxError: Identifier 'a' has already been declared
          let a = 5

let a = 10
// SyntaxError: Identifier 'a' has already been declared

        
        
          
        
      

То же правило работает и при использовании const, и при использовании смешанного подхода:

        
          
          const a = 5const a = 10// SyntaxError: Identifier 'a' has already been declaredvar b = 5const b = 10// SyntaxError: Identifier 'b' has already been declared
          const 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(5)// 5a = 10console.log(a)// 10
          let a = 5
console.log(5)
// 5

a = 10
console.log(a)
// 10

        
        
          
        
      

Стартовое значение const изменить нельзя, будь то примитивное значение:

        
          
          const a = 5a = 10// TypeError: Assignment to constant variable
          const a = 5
a = 10
// TypeError: Assignment to constant variable

        
        
          
        
      

Или ссылка на объект:

        
          
          const obj = {  a: 5,}obj = {  a: 10,}// TypeError: Assignment to constant variable
          const obj = {
  a: 5,
}

obj = {
  a: 10,
}
// TypeError: Assignment to constant variable

        
        
          
        
      

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

        
          
          const obj = {  a: 5,}obj.a = 10console.log(obj)// { a: 10 }
          const obj = {
  a: 5,
}

obj.a = 10

console.log(obj)
// { a: 10 }

        
        
          
        
      

Переменные var

Секция статьи "Переменные var"

Объявление переменных при помощи ключевого слова var было в JavaScript с первых версий.

Объявление

Секция статьи "Объявление"

Переменные var можно объявлять без присвоения им значения, в таком случае они будут равны undefined:

        
          
          var aconsole.log(a)// undefinedvar b = 5console.log(b)// 5
          var 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 defined
          if (true) {
  var a = 5
}

function foo() {
  var b = 10
}

console.log(a)
// 5
console.log(b)
// ReferenceError: b is not defined

        
        
          
        
      

Объявление переменной вне функций делает их глобальными переменными. Они доступны как свойства глобального объекта:

        
          
          var varVariable = 5console.log(window.varVariable)// 5
          var varVariable = 5

console.log(window.varVariable)
// 5

        
        
          
        
      

К переменным, объявленным при помощи ключевого слова var, можно обращаться до момента объявления. В отличие от let и const, ошибки это не вызовет. Такое поведение называется hoisting - «всплытие»:

        
          
          console.log(a)// undefinedvar a = 5console.log(a)// 5
          console.log(a)
// undefined

var a = 5

console.log(a)
// 5

        
        
          
        
      

Разберём, как работает функциональная область видимости:

        
          
          var a = 5function foo() {  console.log(a)  // undefined  var a = 10  console.log(a)  // 10}foo()console.log(a)// 5
          var 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 происходит обращение именно к ней (до её объявления), а не к той, что находится вне функции.

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

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

Дебаггер, JavaScript выполнил строку с инициализацией локальной переменной.
В конце выполнения функции локальной переменной установлено значение, и чтение происходит из неё.

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

Дебаггер, JavaScript выполнил весь код. Локальная переменная уничтожена, доступна только глобальная.
На последнем шаге локальная переменная уже недоступна, так как выполняется код из глобальной области видимости.

Более подробно об этом можно прочитать в отдельной статье

Смена значения в var

Секция статьи "Смена значения в var"

Значение, хранящееся в переменной var, можно изменить двумя способами:

  • обратиться к имени переменной и присвоить новое значение:
        
          
          var a = 5console.log(a)// 5a = 10console.log(a)// 10
          var a = 5
console.log(a)
// 5

a = 10
console.log(a)
// 10

        
        
          
        
      
  • обратиться к имени переменной вместе с ключевым словом var:
        
          
          var a = 5console.log(a)// 5var a = 10console.log(a)// 10
          var a = 5
console.log(a)
// 5

var a = 10
console.log(a)
// 10

        
        
          
        
      

На практике

Секция статьи "На практике"

ruslauz

Секция статьи "ruslauz"

🛠 В новом коде используйте только 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 = 3000const UNAUTHORIZED_CODE = 401
          const BASE_URL = 'https://doka.guide'

const PORT = 3000

const UNAUTHORIZED_CODE = 401