关于javascript:在ES6中是否使用let或const声明变量?

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

我在ES6上玩了一段时间,我注意到,虽然用var声明的变量如预期一样被提升…

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

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

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

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

这是否意味着用letconst声明的变量没有被提升?这里到底发生了什么?在这个问题上,letconst有什么区别吗?


@当然,在声明这些变量之前,它们不能被访问是正确的。不过,这比这要复杂一些。

Are variables declared with let or const not hoisted? What is really going on here?

所有声明(varletconstfunctionfunction*class都在javascript中"挂起"。这意味着,如果在作用域中声明了名称,则在该作用域中,标识符将始终引用该特定变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
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或(generator)函数right初始化。但是,词汇声明的变量保持未初始化状态。这意味着当您试图访问ReferenceError异常时会抛出异常。只有当评估let/const/class语句时,它才会初始化,前面(上面)的所有内容都称为暂时死区。

1
2
3
4
5
6
7
8
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;那样。

时间死区不是句法位置,而是变量(作用域)创建和初始化之间的时间。只要代码没有被执行(例如,函数体或简单的死代码),引用声明上面代码中的变量不是一个错误,并且如果在初始化之前访问变量,即使访问代码低于声明(例如,在也被调用的提升函数声明中),它也会抛出异常早期)。

Is there any difference between let and const in this matter?

不,就起重而言,它们的工作原理是一样的。它们之间的唯一区别是,一个constant必须并且只能在声明的初始化部分中赋值(const one = 1;const one;和后来的赋值(如one = 2都是无效的)。

1:EDOCX1[4]声明仍然只在功能级别工作,当然


引用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.

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


ES6引入了let变量,并给出了block level scoping变量。在ES5之前,我们没有block level scoping,所以块内声明的变量总是hoisted到功能级别范围。

基本上,Scope是指程序中变量的可见位置,它决定了允许在哪里使用已声明的变量。在ES5中,我们有global scope,function scope and try/catch scope,在ES6中,我们也使用let得到块级范围。

  • 当你用var关键字定义一个变量时,它从定义的那一刻起就知道整个函数。
  • 当使用let语句定义变量时,它只在定义的块中知道。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
     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中知道,而不是在之前和之后。然而,我们的变量ientire function中是已知的,从定义之时起。

使用let还有另一个巨大的优势,因为它创建了一个新的词汇环境,并且还绑定了新的值,而不是保留旧的引用。

1
2
3
4
5
6
7
8
9
10
11
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基本相同,唯一的区别是它们的值不能改变。In常量允许突变,但不允许重新分配。

1
2
3
4
5
6
7
8
9
10
11
12
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([])