Переменные, объявленные с помощью let или const, не отображаются в ES6?

266

Я какое-то время играл с 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в этом вопросе?

Любош Турек
источник

Ответы:

346

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

Переменные объявлены с 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декларациями заключается в инициализации .
Первые инициализируются с помощью функции 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;оператор инициализирует переменную с помощью undefinedlike let y = undefined;.

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

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

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

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

Берги
источник
16
Я считаю, что что-то подобное let foo = () => bar; let bar = 'bar'; foo();иллюстрирует, что все декларации являются эффектом подъема еще лучше, потому что это не очевидно из-за временной мертвой зоны.
Настой Эстус
1
Я собирался спросить о ссылке на определение let в функции, объявленной перед let (т.е. замыканием). Я думаю, что это отвечает на вопрос, это законно, но будет ошибкой ref, если функция вызывается до выполнения оператора let, и будет хорошо, если функция будет вызвана позже. возможно, это можно добавить к ответу, если это правда?
Майк Липперт
2
@MikeLippert Да, это правильно. Вы не должны вызывать функцию, которая обращается к переменной до ее инициализации. Этот сценарий происходит, например, с каждым объявлением подъемной функции.
Берги
1
Решение сделать constлайк let- это недостаток дизайна. В пределах области, constдолжны были быть сделаны, чтобы быть поднятыми и точно инициализированными вовремя, когда к нему обращаются. На самом деле, они должны иметь ключевое слово a const, a letи другое, которое создает переменную, которая работает как «только для чтения» let.
Pacerier
1
« Первые инициализируются с помощью undefined …» может быть подходящим для объявлений var, но не подходит для объявлений функций, которым присваивается значение до начала выполнения.
RobG
87

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

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

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

thefourtheye
источник
22

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([])

Thalaivar
источник
5

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

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

console.log(x); // ReferenceError
let x = 3;
YourAboutMeIsBlank
источник
0

в es6, когда мы используем let или const, мы должны объявить переменную перед их использованием. например. 1 -

// this will work
u = 10;
var u;

// this will give an error 
k = 10;
let k;  // ReferenceError: Cannot access 'k' before initialization.

например. 2-

// this code works as variable j is declared before it is used.
function doSmth() {
j = 9;
}
let j;
doSmth();
console.log(j); // 9
user260778
источник