Var, Let, Const - в чем разница?

ES2015 (ES6) принес с собой много новых интересных фич. Теперь на дворе 2020 год, и можно предположить, что многие JavaScript-разработчики уже познакомились с этими фичами и стали их применять. 

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

Одна из фич, появившихся в E6, — это добавление let и const, которые можно использовать, чтобы объявить переменную. Вопрос таков: чем они отличаются от старого-доброго var, которым мы пользовались до этого? Если вам это до сих пор неясно, тогда эта статья для вас.

В этой статье мы обсудим var, let и const  —  их области видимости, использование и поднятие. Пока будете читать, обращайте внимание на отличия между ними, на которые я буду указывать. 

Var

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

Область видимости var

Область видимости по существу своему указывает на то, где данные переменные доступны для использования. Var-объявления могут обладать видимостью глобальной или локальной (область видимости в пределах функции). 

Переменная var является глобальной, когда объявлена вне какой-либо функции. Это означает, что любая переменная, объявленная через var вне блока функции, доступна для использования во всем окне. 

Var является локальной, когда объявлена внутри функции. Это означает, что она доступна и к ней можно обращаться только изнутри этой функции.

Чтобы разобраться получше, давайте рассмотрим пример:

var greeter = "hey hi";
    
    function newFunction() {
        var hello = "hello";
    }

Здесь переменная greeter —  глобальная, поскольку находится вне какой-либо функции, а hello —  ограничена функцией. Так что мы не можем обратиться к переменной hello извне данной функции. Поэтому, если попробовать сделать вот так: 

var tester = "hey hi";
    
    function newFunction() {
        var hello = "hello";
    }
    console.log(hello); // error: hello is not defined

…на выходе будет ошибка. Это происходит из-за того, что переменная hello недоступна вне конкретной функции. 

Переменные var могут быть объявлены повторно или обновлены

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

    var greeter = "hey hi";
    var greeter = "say Hello instead";

Или даже так: 

    var greeter = "hey hi";
    greeter = "say Hello instead";

Поднятие var

Поднятие —  механизм в JavaScript, по которому переменные и объявления функций перемещаются в верхнюю часть своей области видимости, прежде чем начнется выполнение кода. Это означает, что если сделать вот так:

console.log (greeter);
var greeter = "say hello"

…то это будет интерпретировано так: 

var greeter;
console.log(greeter); // greeter is undefined
greeter = "say hello"

Таким образом, переменные var поднимаются в верхнюю часть своей области видимости и инициализируются со значением undefined.

Проблема с var

Есть одна слабость, которая сопутствует var. Чтобы объяснить ее, я приведу пример ниже:

var greeter = "hey hi";
    var times = 4;

    if (times > 3) {
        var greeter = "say Hello instead"; 
    }
    
    console.log(greeter) // "say Hello instead"

Как видно, поскольку times > 3 возвращает значение true, значение переменной greeter переопределяется как “say Hello instead”. Хотя это не проблема, если вы сознательно хотите добиться такого переопределения для greeter. Проблема возникает тогда, когда вы не осознаёте, что переменная greeter уже была определена ранее. 

Если вы использовали greeter в других участках вашего кода, вас удивит результат, который получите на выходе. Это наверняка вызовет множество ошибок в вашем коде. Вот почему возникает необходимость в let и const.

Let

let теперь наиболее предпочтительный вариант для объявления переменных. Что неудивительно: let выступает в качестве улучшения объявления через var, и к тому же решает проблему с var, о которой мы только что говорили. 

let имеет блочную область видимости

Блок — это фрагмент кода, ограниченный фигурными скобками {}. Всё, что находится внутри фигурных скобок, относится к блоку. 

Таким образом, переменная, объявленная в блоке через let, будет доступна только внутри этого блока. Давайте рассмотрим на примере:

let greeting = "say Hi";
   let times = 4;

   if (times > 3) {
        let hello = "say Hello instead";
        console.log(hello);// "say Hello instead"
    }
   console.log(hello) // hello is not defined

Здесь видно, что попытка использовать hello вне блока (фигурных скобок, в рамках которых переменная была определена) возвращает ошибку. Это происходит потому, что переменные типа let являются блочными. 

Переменные let могут быть обновлены, но не объявлены повторно

Точно так же, как и в случае с var, переменные, объявленные через let, можно обновлять внутри их области видимости. Но, в отличие от var, let-переменные нельзя повторно объявить внутри области видимости. Так что, хотя такое сработает: 

    let greeting = "say Hi";
    greeting = "say Hello instead";

этот код уже вернет ошибку:

    let greeting = "say Hi";
    let greeting = "say Hello instead"; // error: Identifier 
'greeting' has already been declared

Однако, если переменная с одним и тем же именем определена в разных областях видимости, ошибки не будет:

let greeting = "say Hi";
    if (true) {
        let greeting = "say Hello instead";
        console.log(greeting); // "say Hello instead"
    }
  console.log(greeting); // "say Hi"

Почему же ошибки нет? Так происходит, потому что оба экземпляра рассматриваются как различные переменные, поскольку у них различаются области видимости. 

Это и делает let лучшей опцией по сравнению с var. Используя let, вам не нужно беспокоиться, не было ли у вас уже переменной с таким именем раньше, ведь переменная существует только внутри своей области видимости. 

Кроме того, поскольку переменная внутри конкретной области видимости может быть объявлена только единожды, проблема, которую мы обсуждали выше относительно var, перестает иметь место. 

Поднятие let

Точно так же, как в случае с var, объявления через let перемещаются вверх. Но в отличие от var-переменных, которые инициализируются как undefined, ключевое слово для let не инициализируется. Так что если вы попытаетесь использовать переменную let до того, как она будет объявлена, то получите ошибку обращения к переменной ReferenceError.

Const

Переменные, объявленные через const, сохраняют постоянные значения. Объявления через const имеют некоторое сходство с объявлениями через let.

const имеет блочную область видимости

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

Переменные const не могут быть ни обновлены, ни объявлены повторно

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

    const greeting = "say Hi";
    greeting = "say Hello instead";// error: Assignment to constant variable.

…ни вот такого:

    const greeting = "say Hi";
    const greeting = "say Hello instead";// error: Identifier 
'greeting' has already been declared

Таким образом, каждое объявление через const должно быть инициализировано в момент объявления. 

Это поведение несколько отличается, когда речь идет об объектах, объявленных через const. Пускай const-объект не может быть обновлен, свойства таких объектов обновлять можно. Поэтому, если объявить const-объект вот таким образом: 

   const greeting = {
        message: "say Hi",
        times: 4
    }

…то, пускай, мы не можем сделать такого: 

const greeting = {
        words: "Hello",
        number: "five"
    } // error:  Assignment to constant variable.

Мы можем сделать другое: 

greeting.message = "say Hello instead";

Это позволит обновить значение greeting.message без того, чтобы получить на выходе ошибку.

Поднятие const

Точно так же, как и let, объявления через const перемещаются вверх, но не инициализируются. 

Так что на случай, если вы упустили суть отличий, то вот они вкратце:

  1. Переменные, объявленные через var, могут быть глобальными или иметь область видимости в рамках функции; let и const имеют блочную область видимости.
  2. var-переменные могут быть как обновлены, так и переопределены внутри области видимости; let-переменные можно обновлять, но не переопределять; const-переменные нельзя ни обновлять, ни переопределять. 
  3. Со всеми ними осуществляется поднятие наверх области видимости. Но если var-переменные при этом инициализируются как undefined, let и const не инициализируются.
  4. В то время как var и let можно объявить, но не инициализировать, const необходимо инициализировать во время объявления.

Читайте также:


Перевод статьи Sarah Chima Atuonwu“Var, Let, and Const — What’s the Difference?”