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

时间:2015-07-04 10:12:38

标签: javascript ecmascript-6 const let hoisting

我一直在玩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声明的变量不会被挂起?这是怎么回事?这件事letconst之间有什么区别吗?

6 个答案:

答案 0 :(得分:290)

@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声明之间的差异是初始化< / strong>即可。
当在作用域顶部创建绑定时,前者使用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;初始化变量。

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

  

此问题letconst之间是否存在差异?

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

1:var声明仍然只在功能级别上工作,当然

答案 1 :(得分:72)

引用ECMAScript 6(ECMAScript 2015)规范&#39; s,let and const declarations部分,

  

变量是在实例化包含词法环境时创建的,但在评估变量的LexicalBinding之前可能无法以任何方式访问

所以,回答你的问题,是的,letconst提升,但在运行时评估实际声明之前你无法访问它们。

答案 2 :(得分:18)

ES6介绍Let提出的block level scoping变量。在ES5之前我们没有block level scoping,所以在块内声明的变量总是hoisted到函数级别范围。

基本上Scope指的是程序中变量可见的位置,它决定了您允许使用已声明的变量的位置。在ES5我们有global 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中已知,而不是之前和之后。然而,我们的变量ientire 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循环始终打印 last 值,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([])

答案 3 :(得分:1)

来自MDN web docs:

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

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

答案 4 :(得分:1)

在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

答案 5 :(得分:1)

根据ECMAScript®2021

Let and Cons声明

  • let和const声明定义了范围为运行中的执行上下文的LexicalEnvironment的变量。
  • 在实例化其包含的环境记录时创建变量,但是在评估变量的LexicalBinding之前,不能以任何方式对其进行访问。
  • 由带有初始化程序的LexicalBinding定义的变量在评估LexicalBinding时被分配其初始化程序的AssignmentExpression的值,在创建变量时不赋值
  • 如果 let声明中的LexicalBinding没有初始化程序,则在计算LexicalBinding时会为变量分配未定义的值

块声明实例化

  • 当评估块或CaseBlock时,将创建一个新的声明性环境记录,并在环境记录中实例化该块中声明的每个块作用域变量,常量,函数或类的绑定。
  • 无论控制如何离开LexicalEnvironment总是将其恢复到其原始状态

顶级词汇声明的名称

在函数或脚本的顶层,将函数声明视为var声明,而不是词汇声明。

结论

  • let和const被吊起但未初始化。

    在变量声明之前在块中引用变量会导致ReferenceError,因为变量从块的开始一直到声明被处理为止,都处于“临时死区”中。 >

下面的示例清楚地说明了“ let”变量在词法作用域/嵌套词法作用域中的行为。

示例1

var a;
console.log(a); //undefined

console.log(b); //undefined
var b;


let x;
console.log(x); //undefined

console.log(y); // Uncaught ReferenceError: y is not defined
let y; 

变量“ y”给出了referenceError,这并不意味着它没有被提升。实例化包含环境时创建该变量。但是,由于它处于无法访问的“临时死区”,因此可能无法访问它。

示例2

let mylet = 'my value';
 
(function() {
  //let mylet;
  console.log(mylet); // "my value"
  mylet = 'local value';
})();

示例3

let mylet = 'my value';
 
(function() {
  let mylet;   
  console.log(mylet); // undefined
  mylet = 'local value';
})();

在示例3中,函数内部新声明的“ mylet”变量在log语句之前没有初始化程序,因此值为“ undefined”。

来源

ECMA MDN