Javascript ES6 中是否未提升使用 let 或 const 声明的变量?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31219420/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 06:23:39  来源:igfitidea点击:

Are variables declared with let or const not hoisted in ES6?

javascriptecmascript-6constlethoisting

提问by Lubo? Turek

I have been playing with ES6 for a while and I noticed that while variables declared with varare hoisted as expected...

我一直在玩 ES6 一段时间,我注意到虽然声明的变量var按预期提升......

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

...variables declared with letor constseem to have some problems with hoisting:

...声明的变量letconst似乎在提升方面存在一些问题:

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

and

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

Does this mean that variables declared with letor constare not hoisted? What is really going on here? Is there any difference between letand constin this matter?

这是否意味着使用let或未const提升声明的变量?这里到底发生了什么?在这件事上let和之间有什么区别const吗?

回答by Bergi

@thefourtheye is correct in saying that these variables cannot be accessedbefore they are declared. However, it's a bit more complicated than that.

@thefourtheye 说这些变量在声明之前无法访问是正确的。然而,它比那要复杂一些。

Are variables declared with letor constnot hoisted? What is really going on here?

与声明的变量letconst不悬挂?这里到底发生了什么?

All declarations(var, let, const, function, function*, class) are "hoisted"in JavaScript. This means that if a name is declared in a scope, in that scope the identifier will always reference that particular variable:

所有声明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;
}

This is true both for function and block scopes1.

对于函数作用域和块作用域1都是如此。

The difference between var/function/function*declarations and let/const/classdeclara­tions is the initialisation.
The former are initialised with undefinedor the (generator) function right when the binding is created at the top of the scope. The lexically declared variables however stay uninitialised. This means that a ReferenceErrorexception is thrown when you try to access it. It will only get initialised when the let/const/classstatement is evaluated, everything before (above) that is called the temporal dead zone.

之间的差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";
}());

Notice that a let y;statement initialises the variable with undefinedlike let y = undefined;would have.

请注意,let y;语句会使用undefinedlikelet y = undefined;来初始化变量。

The temporaldead zone is not a syntactic location, but rather the timebetween the variable (scope) creation and the initialisation. It's not an error to reference the variable in code above the declaration as long as that code is not executed (e.g. a function body or simply dead code), and it will throw an exception if you access the variable before the initialisation even if the accessing code is below the declaration (e.g. in a hoisted function declaration that is called too early).

时间盲区不是语法的位置,而是时间的变量(范围)的创建和初始化之间。只要不执行该代码(例如函数体或简单的死代码),在声明上方的代码中引用变量就不是错误,并且如果您在初始化之前访问该变量,即使访问代码位于声明下方(例如,在过早调用的提升函数声明中)。

Is there any difference between letand constin this matter?

在这件事上let和之间有什么区别const吗?

No, they work the same as far as hoisting is regarded. The only difference between them is that a constant must be and can only be assigned in the initialiser part of the declaration (const one = 1;, both const one;and later reassignments like one = 2are invalid).

不,就提升而言,它们的工作原理相同。它们之间的唯一区别是const蚂蚁必须并且只能在声明的初始化部分分配(const one = 1;,两者const one;和以后的重新分配one = 2都是无效的)。

1: vardeclarations are still working only on the function level, of course

1:当然,var声明仍然只在函数级别起作用

回答by thefourtheye

Quoting ECMAScript 6 (ECMAScript 2015) specification's, letand constdeclarationssection,

引用 ECMAScript 6 (ECMAScript 2015) 规范letconst声明部分,

The variables are created when their containing Lexical Environment is instantiated but may not be accessed in any way until the variable's LexicalBinding is evaluated.

变量在其包含的 Lexical Environment 被实例化时创建,但在评估变量的 LexicalBinding 之前不能以任何方式访问

So, to answer your question, yes, letand consthoist but you cannot access them before the actual declaration is evaluated at runtime.

因此,要回答您的问题,是的,let并且const提升但在运行时评估实际声明之前您无法访问它们。

回答by Thalaivar

ES6introduces Letvariables which comes up with block level scoping. Until ES5we did not have block level scoping, so the variables which are declared inside a block are always hoistedto function level scoping.

ES6引入Letblock level scoping. 直到ES5我们没有block level scoping,所以在块内声明的变量始终hoisted是函数级别的范围。

Basically Scoperefers to where in your program your variables are visible, which determines where you are allowed to use variables you have declared. In ES5we have global scope,function scope and try/catch scope, with ES6we also get the block level scoping by using Let.

基本上Scope是指您的变量在程序中的可见位置,这决定了您可以在何处使用已声明的变量。在ES5我们有global scope,function scope and try/catch scope, withES6我们还通过使用 Let 获得块级范围。

  • When you define a variable with varkeyword, it's known the entire function from the moment it's defined.
  • When you define a variable with letstatement it's only known in the block it's defined.

     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"]);
    
  • 当您使用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"]);
    

If you run the code, you could see the variable jis only known in the loopand not before and after. Yet, our variable iis known in the entire functionfrom the moment it is defined onward.

如果你运行代码,你可以看到变量j只在 the 中,loop而不是 before 和 after 中。然而,我们的变量从定义的那一刻起i就是已知的entire function

There is another great advantage using let as it creates a new lexical environment and also binds fresh value rather than keeping an old reference.

使用 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)
}

The first forloop always print the lastvalue, with letit creates a new scope and bind fresh values printing us 1, 2, 3, 4, 5.

第一个for循环总是打印最后一个值,let它创建一个新的作用域并绑定打印 us 的新值1, 2, 3, 4, 5

Coming to constants, it work basically like let, the only difference is their value can't be changed. In constants mutation is allowed but reassignment is not allowed.

来到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);

If a constant refers to an object, it will always refer to the objectbut the objectitself can be changed (if it is mutable). If you like to have an immutable object, you could use Object.freeze([])

如果常量引用 an object,它将始终引用 theobject但它object本身可以更改(如果它是可变的)。如果你喜欢不可变的object,你可以使用Object.freeze([])

回答by YourAboutMeIsBlank

From MDN web docs:

来自MDN 网络文档:

In ECMAScript 2015, letand constare hoisted but not initialized. Referencing the variable in the block before the variable declaration results in a ReferenceErrorbecause the variable is in a "temporal dead zone" from the start of the block until the declaration is processed.

在2015年的ECMAScript,letconst高挂,但没有初始化。在变量声明之前引用块中的变量会导致 a,ReferenceError因为从块的开始直到处理声明,变量都处于“时间死区”中。

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