Переменные, объявленные с let или const, не подняты в ES6?

Я играл с ES6 некоторое время, и я заметил, что, хотя переменные, объявленные с помощью var, поднимаются, как ожидалось...

console.log(typeof name); // undefined
var name = "John";

... переменные, объявленные с помощью let или const, похоже, имеют некоторые проблемы с подъемом:

console.log(typeof name); // ReferenceError
let name = "John";

и

console.log(typeof name); // ReferenceError
const name = "John";

Означает ли это, что переменные, объявленные с помощью let или const, не были подняты? Что здесь происходит? Есть ли разница между let и const в этом вопросе?

Ответы

Ответ 1

@thethethethe верно, говоря, что эти переменные не могут быть доступны, прежде чем они будут объявлены. Однако это немного сложнее, чем это.

Не объявлены ли переменные, объявленные с помощью let или const? Что здесь происходит?

Все объявления (var, let, const, function, function*, class) "подняты" в JavaScript, Это означает, что если имя объявлено в области видимости, в этой области идентификатор всегда ссылается на эту конкретную переменную:

x = "global";
// function scope:
(function() {
    x; // not "global"

    var/let/… x;
}());
// block scope (not for `var`s):
{
    x; // not "global"

    let/const/… x;
}

Это верно как для области функций, так и для блоков 1.

Разница между объявлениями var/function/function* и let/const/class declara & shy;, это инициализация .
Первые инициализируются с помощью функции undefined или (генератора) справа, когда привязка создается в верхней части области. Однако лексически объявленные переменные остаются неинициализированными. Это означает, что при попытке доступа к нему генерируется исключение ReferenceError. Он будет инициализироваться только тогда, когда вычисляется оператор let/const/class, все до (выше), которое называется временной мертвой зоной.

x = y = "global";
(function() {
    x; // undefined
    y; // Reference error: y is not defined

    var x = "local";
    let y = "local";
}());

Обратите внимание, что оператор let y; инициализирует переменную с undefined, например let y = undefined;.

Временная мертвая зона не является синтаксическим расположением, а скорее временем между созданием переменной (области) и инициализацией. Это не ошибка ссылки на переменную в коде выше объявления, если этот код не выполняется (например, тело функции или просто мертвый код), и он будет генерировать исключение, если вы получите доступ к переменной до инициализации, даже если доступ код ниже декларации (например, в объявлении с функцией hoisted, которое называется слишком ранним).

Есть ли разница между let и const в этом вопросе?

Нет, они работают так же, как и подъем. Единственная разница между ними заключается в том, что const ant должен быть назначен и может быть назначен только в части инициализации объявления (const one = 1;, как const one;, так и более поздние переопределения, такие как one = 2).

1: var объявления все еще работают только на уровне функции, конечно

Ответ 2

Цитирование спецификации ECMAScript 6 (ECMAScript 2015) спецификации, let и const,

Переменные создаются, когда создается экземпляр их содержащей Lexical Environment, но к ним нельзя обращаться каким-либо образом, пока не будет оценена переменная LexicalBinding.

Итак, чтобы ответить на ваш вопрос, да, let и const hoist, но вы не можете получить к ним доступ, пока фактическое объявление не будет оценено во время выполнения.

Ответ 3

ES6 вводит переменные Let, которые появляются с block level scoping. До ES5 у нас не было block level scoping, поэтому переменные, объявленные внутри блока, всегда hoisted для определения уровня уровня.

В основном Scope относится к тому, где в вашей программе видны ваши переменные, что определяет, где вам разрешено использовать объявленные вами переменные. В ES5 имеем global scope,function scope and try/catch scope, при ES6 мы также получаем область охвата уровня блока, используя Let.

  • Когда вы определяете переменную с ключевым словом var, она знает всю функцию с момента ее определения.
  • Когда вы определяете переменную с выражением Let, она известна только в определенном блоке.

     function doSomething(arr){
         //i is known here but undefined
         //j is not known here
    
         console.log(i);
         console.log(j);
    
         for(var i=0; i<arr.length; i++){
             //i is known here
         }
    
         //i is known here
         //j is not known here
    
         console.log(i);
         console.log(j);
    
         for(let j=0; j<arr.length; j++){
             //j is known here
         }
    
         //i is known here
         //j is not known here
    
         console.log(i);
         console.log(j);
     }
    
     doSomething(["Thalaivar", "Vinoth", "Kabali", "Dinesh"]);
    

Если вы запустите код, вы увидите, что переменная j известна только в loop, а не до и после. Тем не менее наша переменная i известна в entire function с момента ее определения вперед.

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

for(var i=1; i<6; i++){
   setTimeout(function(){
      console.log(i);
   },1000)
}

for(let i=1; i<6; i++){
   setTimeout(function(){
      console.log(i);
   },1000)
}

Первый цикл for всегда печатает последнее значение, при Let он создает новую область и связывает новые значения, печатая нас 1, 2, 3, 4, 5.

Приходя в constants, он работает в основном как Let, единственное отличие в том, что их значение не может быть изменено. В константах допускается мутация, но переназначение не разрешено.

const foo = {};
foo.bar = 42;
console.log(foo.bar); //works

const name = []
name.push("Vinoth");
console.log(name); //works

const age = 100;
age = 20; //Throws Uncaught TypeError: Assignment to constant variable.

console.log(age);

Если константа ссылается на object, она всегда будет ссылаться на object, но сам object может быть изменен (если он изменен). Если вам нравится иметь неизменяемый object, вы можете использовать Object.freeze([])

Ответ 4

Из веб-документов MDN:

В ECMAScript 2015 let и const поднимаются, но не инициализируются. Ссылка на переменную в блоке до ее объявления приводит к возникновению ReferenceError поскольку переменная находится в "временной мертвой зоне" с начала блока до обработки объявления.

console.log(x); // ReferenceError
let x = 3;

Ответ 5

  • В случае var, после создания определений переменных, перед выполнением построчно каждая из переменных инициализируется с неопределенным значением.
  • В случае let/const, инициализация в undefined не происходит до тех пор, пока не произойдет строка, на которой фактически происходит объявление.

Доступ к переменной до инициализации приводит к ReferenceError. Переменная находится во "временной мертвой зоне" от начала блока до обработки инициализации.

console.log(typeof name); // No intialization till now => referenceError
let name = "John";