ES6中是否不使用let或const声明的变量?


266

我玩ES6已有一段时间了,我注意到虽然用声明的变量var按预期悬挂了...

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

... 用提升声明letconst似乎有一些提升问题的变量:

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

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

这是否意味着使用letconst不声明的变量被吊起?这到底是怎么回事?是否有任何区别let,并const在此问题?

Answers:


346

@thefourtheye的正确说法是在声明这些变量之前无法访问它们。但是,这要复杂得多。

是否用letconst不声明变量?这到底是怎么回事?

所有声明varletconstfunctionfunction*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();说明可以更好地说明所有声明的提升效果,因为由于暂时的死区而导致的效果不明显。
Estus Flask

1
我要问的是在let之前声明的函数(即闭包)中引用let定义。我认为这可以回答问题,这是合法的,但是如果在执行let语句之前调用该函数将是ref错误,如果在之后调用该函数会很好。如果是这样,也许可以将其添加到答案中?
Mike Lippert

2
@MikeLippert是的,这是正确的。在初始化变量之前,请勿调用访问该变量的函数。例如,这种情况发生在每个提升的函数声明中。
Bergi

1
做出const喜欢的决定let是设计缺陷。在范围内,const应在访问它时将其吊起并及时初始化。确实,他们应该有一个const,a let和另一个关键字,它们创建一个类似于“ readonly”的变量let
Pacerier's

1
对于var声明来说,“ 前者用未定义的初始化 ”可能是可以的,但似乎不适用于函数声明,函数声明在执行开始之前已分配了一个值。
RobG

87

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

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

因此,要回答您的问题,是的,let然后将其const吊起,但是您无法在运行时对实际声明进行求值之前访问它们。


22

ES6介绍Let附带的变量block level scoping。直到ES5我们没有了block level scoping,所以在块内声明的变量始终处于hoisted函数级作用域。

基本上Scope是指变量在程序中的可见位置,它决定了允许在何处使用已声明的变量。在ES5global scope,function scope and try/catch scopeES6我们还使用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创建一个新的作用域并绑定新值以打印us 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([])


5

MDN网站文档中:

在ECMAScript 2015中,let并且const已吊起但未初始化。在变量声明之前引用块中的变量会导致a,ReferenceError因为变量从块的开始一直到声明被处理为止都处于“临时死区”。

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

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.