ECMAScript 6 推出 let 陈述.

我听说它被描述为“局部”变量,但我仍然不太确定它的行为与 var 关键词。

有什么区别?什么时候应该 let 被使用过 var?

有帮助吗?

解决方案

的范围规则

主要区别是作用域规则。通过var关键字声明的变量可以被限制在直接功能体(因此功能范围),同时let变量被限定在直接包围块由{ }(因此块范围)表示。

function run() {
  var foo = "Foo";
  let bar = "Bar";

  console.log(foo, bar);

  {
    let baz = "Bazz";
    console.log(baz);
  }

  console.log(baz); // ReferenceError
}

run();

为什么let关键字被引入到语言的原因是功能范围混乱和是错误的在JavaScript的主要来源之一。

看看这个例子中从另一个计算器问题

var funcs = [];
// let's create 3 functions
for (var i = 0; i < 3; i++) {
  // and store them in funcs
  funcs[i] = function() {
    // each should log its value.
    console.log("My value: " + i);
  };
}
for (var j = 0; j < 3; j++) {
  // and now let's run each one to see
  funcs[j]();
}

My value: 3是输出到控制台每次funcs[j]();被调用因为匿名函数被绑定到相同的相同的变量。

人们不得不创建创建立即调用的函数,以捕获从循环正确的值但也有毛。

起重

当用var关键字声明变量“悬挂”于块的顶部,这意味着它们在其封闭范围可访问声明它们甚至之前:

function run() {
  console.log(foo); // undefined
  var foo = "Foo";
  console.log(foo); // Foo
}

run();

让变量不被初始化,直到它们的定义进行评价。在ReferenceError初始化结果之前访问它们。可变说是在从块的开头“颞死区”,直到初始化处理。

function checkHoisting() {
  console.log(foo); // ReferenceError
  let foo = "Foo";
  console.log(foo); // Foo
}

checkHoisting();

创建全局对象属性

在顶层,let,不像var,不全局对象上创建一个属性:

var foo = "Foo";  // globally scoped
let bar = "Bar"; // globally scoped

console.log(window.foo); // Foo
console.log(window.bar); // undefined

重新说明

在严格模式,var会让在同一范围内你重新声明相同的变量而let引发一个SyntaxError。

'use strict';
var foo = "foo1';
var foo = "foo2'; // No problem, 'foo' is replaced.

let bar = "bar1";
let bar = "bar2"; // SyntaxError: Identifier 'bar' has already been declared

其他提示

let也可以使用,以避免与封闭件的问题。其结合的新鲜值而不是保持一个旧参考如示于以下实施例。

for(var i=1; i<6; i++) {
  $("#div" + i).click(function () { console.log(i); });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="div1">1</div>
<div id="div2">2</div>
<div id="div3">3</div>
<div id="div4">4</div>
<div id="div5">5</div>

上面的代码演示了一个经典的JavaScript封闭问题。参照i变量被存储在点击处理程序闭合,而不是i的实际值。

每一个点击处理程序将指向同一个对象,因为有只持有6所以你每次点击拿到六分之一的计数器对象。

一个一般解决方法是在一个匿名函数来包装本并通过i作为参数。也可以通过使用如示于下面的代码代替let现在var可以避免这样的问题。

<子>(测试在铬和Firefox 50)

for(let i=1; i<6; i++) {
  $("#div" + i).click(function () { console.log(i); });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="div1">1</div>
<div id="div2">2</div>
<div id="div3">3</div>
<div id="div4">4</div>
<div id="div5">5</div>

有什么区别 letvar?

  • 使用 a 定义的变量 var 声明是众所周知的 功能 它是在函数开头定义的。*
  • 使用 a 定义的变量 let 声明仅在 街区 从它被定义的那一刻起,它就被定义了。**

要理解差异,请考虑以下代码:

// i IS NOT known here
// j IS NOT known here
// k IS known here, but undefined
// l IS NOT known here

function loop(arr) {
    // i IS known here, but undefined
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( var i = 0; i < arr.length; i++ ) {
        // i IS known here, and has a value
        // j IS NOT known here
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( let j = 0; j < arr.length; j++ ) {
        // i IS known here, and has a value
        // j IS known here, and has a value
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here
}

loop([1,2,3,4]);

for( var k = 0; k < arr.length; k++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS NOT known here
};

for( let l = 0; l < arr.length; l++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS known here, and has a value
};

loop([1,2,3,4]);

// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here

在这里,我们可以看到我们的变量 j 仅在第一个 for 循环中已知,而不是之前和之后。然而,我们的变量 i 在整个函数中是已知的。

另外,请考虑块作用域变量在声明之前是未知的,因为它们没有被提升。您也不允许在同一块内重新声明同一块作用域变量。这使得块作用域变量比全局或函数作用域变量更不容易出错,全局或函数作用域变量被提升并且在多个声明的情况下不会产生任何错误。


使用安全吗 let 今天?

有些人会争辩说,将来我们将只使用 let 语句,而 var 语句将变得过时。JavaScript 大师 凯尔·辛普森 写道 一篇非常详尽的文章解释了为什么他认为情况不会如此.

然而今天,情况绝对不是这样了。事实上,我们实际上需要问自己使用它是否安全 let 陈述。该问题的答案取决于您的环境:

  • 如果您正在编写服务器端 JavaScript 代码(Node.js),您可以安全地使用 let 陈述。

  • 如果您正在编写客户端 JavaScript 代码并使用基于浏览器的转译器(例如 特雷乌尔 或者 babel-独立版),您可以安全地使用 let 声明,但是您的代码在性能方面可能不是最佳的。

  • 如果您正在编写客户端 JavaScript 代码并使用基于 Node 的转译器(例如 跟踪 shell 脚本 或者 巴别塔),您可以安全地使用 let 陈述。而且由于您的浏览器只会了解转译的代码,因此性能缺陷应该是有限的。

  • 如果您正在编写客户端 JavaScript 代码并且不使用转译器,则需要考虑浏览器支持。

    还有一些浏览器不支持 let 完全:

Support table


如何跟踪浏览器支持

有关哪些浏览器支持的最新概述 let 您阅读此答案时的声明,请参阅 Can I Use.


* 全局和函数作用域的变量可以在声明之前初始化和使用,因为 JavaScript 变量是 吊起的. 这意味着声明总是位于范围的顶部。

** 块作用域变量不会被提升

下面是一个解释。

  

let工作方式非常类似于var。的主要区别在于一个var变量的范围是整个封闭函数

维基百科上此表示出了浏览器支持Javascript 1.7。

请注意,只有Mozilla和铬的浏览器支持它。 IE,Safari和潜在的其他人没有。

接受的答案缺少点:

{
  let a = 123;
};

console.log(a); // ReferenceError: a is not defined

let

块作用域

使用声明的变量 let 关键字是块作用域的,这意味着它们仅在 堵塞 他们在其中被宣布。

在顶层(函数之外)

在顶层,变量声明使用 let 不要在全局对象上创建属性。

var globalVariable = 42;
let blockScopedVariable = 43;

console.log(globalVariable); // 42
console.log(blockScopedVariable); // 43

console.log(this.globalVariable); // 42
console.log(this.blockScopedVariable); // undefined

函数内部

在函数内部(但在块外部), let 具有相同的范围 var.

(() => {
  var functionScopedVariable = 42;
  let blockScopedVariable = 43;

  console.log(functionScopedVariable); // 42
  console.log(blockScopedVariable); // 43
})();

console.log(functionScopedVariable); // ReferenceError: functionScopedVariable is not defined
console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined

在一个块内

声明的变量使用 let 块内部无法在该块外部访问。

{
  var globalVariable = 42;
  let blockScopedVariable = 43;
  console.log(globalVariable); // 42
  console.log(blockScopedVariable); // 43
}

console.log(globalVariable); // 42
console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined

在循环内

声明的变量 let in 循环只能在该循环内部引用。

for (var i = 0; i < 3; i++) {
  var j = i * 2;
}
console.log(i); // 3
console.log(j); // 4

for (let k = 0; k < 3; k++) {
  let l = k * 2;
}
console.log(typeof k); // undefined
console.log(typeof l); // undefined
// Trying to do console.log(k) or console.log(l) here would throw a ReferenceError.

带闭合的循环

如果你使用 let 代替 var 在循环中,每次迭代都会得到一个新变量。这意味着您可以安全地在循环内使用闭包。

// Logs 3 thrice, not what we meant.
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}

// Logs 0, 1 and 2, as expected.
for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log(j), 0);
}

颞死区

因为 颞死区, ,使用声明的变量 let 在声明之前无法访问。尝试这样做会引发错误。

console.log(noTDZ); // undefined
var noTDZ = 43;
console.log(hasTDZ); // ReferenceError: hasTDZ is not defined
let hasTDZ = 42;

无需重新申报

您不能使用多次声明同一变量 let. 。您也不能使用声明变量 let 与使用声明的另一个变量具有相同的标识符 var.

var a;
var a; // Works fine.

let b;
let b; // SyntaxError: Identifier 'b' has already been declared

var c;
let c; // SyntaxError: Identifier 'c' has already been declared

const

const 非常类似于 let——它是块作用域的并且有 TDZ。然而,有两点是不同的。

无需重新分配

变量声明使用 const 不能重新分配。

const a = 42;
a = 43; // TypeError: Assignment to constant variable.

请注意,这并不意味着该值是不可变的。它的属性仍然可以改变。

const obj = {};
obj.a = 42;
console.log(obj.a); // 42

如果你想要一个不可变的对象,你应该使用 Object.freeze().

需要初始化器

使用声明变量时始终必须指定一个值 const.

const a; // SyntaxError: Missing initializer in const declaration

以下是两者之间差异的示例(刚刚开始支持 chrome):
enter image description here

正如你所看到的 var j 变量仍然具有 for 循环范围(块范围)之外的值,但是 let i 变量在 for 循环范围之外未定义。

"use strict";
console.log("var:");
for (var j = 0; j < 2; j++) {
  console.log(j);
}

console.log(j);

console.log("let:");
for (let i = 0; i < 2; i++) {
  console.log(i);
}

console.log(i);

有一些细微的差别 - let作用域表现得更像变量范围确实在更多或更少的任何其它语言。

e.g。它范围来封闭块,他们在声明之前,他们根本不存在等。

然而,值得注意的是,let仅更新JavaScript实现的一部分,并且具有不同程度的浏览器支持

  • 变量不提升

    let 将要 不提升 它们出现的块的整个范围。相比之下, var 可以如下吊装。

    {
       console.log(cc); // undefined. Caused by hoisting
       var cc = 23;
    }
    
    {
       console.log(bb); // ReferenceError: bb is not defined
       let bb = 23;
    }
    

    事实上,根据@Bergi, 两个都 varlet 被吊起.

  • 垃圾收集

    块范围 let 对于回收内存的闭包和垃圾收集很有用。考虑,

    function process(data) {
        //...
    }
    
    var hugeData = { .. };
    
    process(hugeData);
    
    var btn = document.getElementById("mybutton");
    btn.addEventListener( "click", function click(evt){
        //....
    });
    

    click 处理程序回调不需要 hugeData 根本没有变量。理论上来说,之后 process(..) 运行,庞大的数据结构 hugeData 可能会被垃圾收集。然而,某些 JS 引擎可能仍然需要保留这个庞大的结构,因为 click 函数在整个范围内都有一个闭包。

    然而,块作用域可以使这个巨大的数据结构被垃圾收集。

    function process(data) {
        //...
    }
    
    { // anything declared inside this block can be garbage collected
        let hugeData = { .. };
        process(hugeData);
    }
    
    var btn = document.getElementById("mybutton");
    btn.addEventListener( "click", function click(evt){
        //....
    });
    
  • let 循环

    let 在循环中可以 重新绑定它 对于循环的每次迭代,确保重新为其分配上一次循环迭代结束时的值。考虑,

    // print '5' 5 times
    for (var i = 0; i < 5; ++i) {
        setTimeout(function () {
            console.log(i);
        }, 1000);  
    }
    

    但是,更换 varlet

    // print 1, 2, 3, 4, 5. now
    for (let i = 0; i < 5; ++i) {
        setTimeout(function () {
            console.log(i);
        }, 1000);  
    }
    

    因为 let 使用这些名称创建一个新的词法环境 a) 初始化表达式 b) 每次迭代(之前用于评估增量表达式),更多详细信息是 这里.

的主要区别是在范围差异,而可让可以是仅在范围内可用的它被声明,像在for循环中,<强> VAR 可以环路例如外部访问。从 MDN (实例文档也从MDN):

  

可让可让你声明了在范围上不限于所述块,语句,或在其上使用的表达变量。这不同于在 VAR 关键字,而不管其限定了可变全球范围内,或局部地向整个功能块范围。

     

通过声明的变量的具有如它们的范围中,如在任何包含的子块它们被定义,以及该块。通过这种方式,可让工作方式非常类似于 VAR 。的主要区别在于,的范围的 VAR 变量是整个封闭函数:

function varTest() {
  var x = 1;
  if (true) {
    var x = 2;  // same variable!
    console.log(x);  // 2
  }
  console.log(x);  // 2
}

function letTest() {
  let x = 1;
  if (true) {
    let x = 2;  // different variable
    console.log(x);  // 2
  }
  console.log(x);  // 1
}`
  

目前的程序和功能的顶级水平,可让,不像 VAR ,不创建全局对象上的属性。例如:

var x = 'global';
let y = 'global';
console.log(this.x); // "global"
console.log(this.y); // undefined
  

当一个块内使用的,让限制变量的范围,以该块。注意之间的差的 VAR 其范围是在声明在函数内部。

var a = 1;
var b = 2;

if (a === 1) {
  var a = 11; // the scope is global
  let b = 22; // the scope is inside the if-block

  console.log(a);  // 11
  console.log(b);  // 22
} 

console.log(a); // 11
console.log(b); // 2

另外,不要忘记它的ECMA6功能,所以它也不是完全支持,所以最好永远transpiles它用巴贝尔等,以ECMA5 ...更多信息有关访问的巴别网站

下面是对添加到别人已经写了一个例子。假设你想的功能,adderFunctions,其中每个函数只有一个数值参数和返回参数和数组中的功能的指数的总和的阵列。试图生成与使用adderFunctions关键字将不能工作方式有人可能会天真地指望一个循环var

// An array of adder functions.
var adderFunctions = [];

for (var i = 0; i < 1000; i++) {
  // We want the function at index i to add the index to its argument.
  adderFunctions[i] = function(x) {
    // What is i bound to here?
    return x + i;
  };
}

var add12 = adderFunctions[12];

// Uh oh. The function is bound to i in the outer scope, which is currently 1000.
console.log(add12(8) === 20); // => false
console.log(add12(8) === 1008); // => true
console.log(i); // => 1000

// It gets worse.
i = -8;
console.log(add12(8) === 0); // => true

上述过程,因为i的范围超出在其中创建每个功能的for块的迭代不产生功能的所需的阵列。相反,在循环结束时,在每个功能的关闭的ii的价值在环(1000)在adderFunctions每一个匿名函数的结尾。这不是我们想要的所有:我们现在有1000种不同功能的阵列在内存中完全一样的行为。如果我们随后更新i的值,该突变会影响到所有的adderFunctions

然而,我们可以使用let关键字再试:

// Let's try this again.
// NOTE: We're using another ES6 keyword, const, for values that won't
// be reassigned. const and let have similar scoping behavior.
const adderFunctions = [];

for (let i = 0; i < 1000; i++) {
  // NOTE: We're using the newer arrow function syntax this time, but 
  // using the "function(x) { ..." syntax from the previous example 
  // here would not change the behavior shown.
  adderFunctions[i] = x => x + i;
}

const add12 = adderFunctions[12];

// Yay! The behavior is as expected. 
console.log(add12(8) === 20); // => true

// i's scope doesn't extend outside the for loop.
console.log(i); // => ReferenceError: i is not defined

这个时间,i是反弹的for循环的每次迭代。每个功能现在保持i的在函数的创建的时间的值,并且adderFunctions按预期运行

现在,图像混合的两种行为,你可能会看到它为什么不推荐到新letconst在同一个脚本老var混合。这样做可能导致一些壮观混淆代码。

const doubleAdderFunctions = [];

for (var i = 0; i < 1000; i++) {
    const j = i;
    doubleAdderFunctions[i] = x => x + i + j;
}

const add18 = doubleAdderFunctions[9];
const add24 = doubleAdderFunctions[12];

// It's not fun debugging situations like this, especially when the
// code is more complex than in this example.
console.log(add18(24) === 42); // => false
console.log(add24(18) === 42); // => false
console.log(add18(24) === add24(18)); // => false
console.log(add18(24) === 2018); // => false
console.log(add24(18) === 2018); // => false
console.log(add18(24) === 1033); // => true
console.log(add24(18) === 1030); // => true

不要让这发生在你身上。使用棉绒。

  

注意:这是一个旨在展示在环路并与函数闭包,这也将是很容易理解的var / let行为教学例子。这将是加数字的可怕的方式。但捕捉匿名函数关闭数据的通用技术可能在现实世界中可能遇到在其他情况下。 YMMV。

区别在于 范围 每个声明的变量的数量。

在实践中,范围的差异会带来许多有用的后果:

  1. let 变量仅在其内部可见 最近的包围 堵塞 ({ ... }).
  2. let 变量只能在发生的代码行中使用 变量已声明(即使 他们被吊起!).
  3. let 变量不能被后续的重新声明 var 或者 let.
  4. 全球的 let 变量没有添加到全局 window 目的。
  5. let 变量是 便于使用 有闭包(它们不会导致 竞争条件).

施加的限制 let 降低变量的可见性并增加尽早发现意外名称冲突的可能性。这使得跟踪和推理变量变得更加容易,包括它们的变量 可达性(帮助回收未使用的内存)。

最后, let 当在大型程序中使用变量或以新的和意想不到的方式组合独立开发的框架时,变量不太可能引起问题。

var 如果您确定在循环中使用闭包 (#5) 或在代码中声明外部可见的全局变量 (#4) 时想要单绑定效果,则可能仍然有用。用于 var 出口可能会被取代,如果 export 从转译器空间迁移到核心语言。

例子

1.在最近的封闭块之外不使用:该代码块将引发引用错误,因为第二次使用 x 发生在声明它的块之外 let:

{
    let x = 1;
}
console.log(`x is ${x}`);  // ReferenceError during parsing: "x is not defined".

相反,同样的例子 var 作品。

2.声明前不使用:
该代码块将抛出一个 ReferenceError 在代码可以运行之前,因为 x 在声明之前使用:

{
    x = x + 1;  // ReferenceError during parsing: "x is not defined".
    let x;
    console.log(`x is ${x}`);  // Never runs.
}

相反,同样的例子 var 解析并运行,不会引发任何异常。

3.无需重新声明:以下代码演示了使用以下方式声明的变量 let 以后不能重新声明:

let x = 1;
let x = 2;  // SyntaxError: Identifier 'x' has already been declared

4.全局变量未附加到 window:

var button = "I cause accidents because my name is too common.";
let link = "Though my name is common, I am harder to access from other JS files.";
console.log(link);  // OK
console.log(window.link);  // undefined (GOOD!)
console.log(window.button);  // OK

5.易于使用闭包:声明的变量 var 不适用于循环内的闭包。这是一个简单的循环,输出变量的值序列 i 在不同的时间点有:

for (let i = 0; i < 5; i++) {
    console.log(`i is ${i}`), 125/*ms*/);
}

具体来说,输出:

i is 0
i is 1
i is 2
i is 3
i is 4

在 JavaScript 中,我们经常在变量创建之后才使用变量。当我们通过传递闭包来延迟输出来演示这一点时 setTimeout:

for (let i = 0; i < 5; i++) {
    setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}

...只要我们坚持,输出就保持不变 let. 。相反,如果我们使用 var i 反而:

for (var i = 0; i < 5; i++) {
    setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}

...该循环意外地输出“i is 5”五次:

i is 5
i is 5
i is 5
i is 5
i is 5

可以在以下两种功能示区别:

function varTest() {
    var x = 31;
    if (true) {
        var x = 71;  // Same variable!
        console.log(x);  // 71
    }
    console.log(x);  // 71
}

function letTest() {
    let x = 31;
    if (true) {
        let x = 71;  // Different variable
        console.log(x);  // 71
    }
    console.log(x);  // 31
}

let是有趣的,因为它使我们能够做这样的事情:

(() => {
    var count = 0;

    for (let i = 0; i < 2; ++i) {
        for (let i = 0; i < 2; ++i) {
            for (let i = 0; i < 2; ++i) {
                console.log(count++);
            }
        }
    }
})();

这导致计数[0,7]。

尽管

(() => {
    var count = 0;

    for (var i = 0; i < 2; ++i) {
        for (var i = 0; i < 2; ++i) {
            for (var i = 0; i < 2; ++i) {
                console.log(count++);
            }
        }
    }
})();

只有计数[0,1]。

功能VS块范围:

var let之间的主要区别在于,与var声明的变量是的函数作用域即可。而用let声明功能的块作用域即可。例如:

function testVar () {
  if(true) {
    var foo = 'foo';
  }

  console.log(foo);
}

testVar();  
// logs 'foo'


function testLet () {
  if(true) {
    let bar = 'bar';
  }

  console.log(bar);
}

testLet(); 
// reference error
// bar is scoped to the block of the if statement 

<强>使用var变量:

在第一功能testVar被称为变量foo的,与var宣布,目前仍是if声明外部访问。此变量foo将是可用的的到处testVar的范围的功能

<强>使用let变量:

当第二功能testLet被称为变量酒吧,let声明的,仅仅是if语句内访问。因为与let声明的变量的块作用域(其中块是大括号e.g if{}for{}function{}之间的代码)。

let变量不要让悬挂:

varlet的另一个区别是与let声明的变量的不要让悬挂。一个例子是为了说明此问题的最佳方式:

let 变量不获取悬挂:

console.log(letVar);

let letVar = 10;
// referenceError, the variable doesn't get hoisted

var变量的获取悬挂:

console.log(varVar);

var varVar = 10;
// logs undefined, the variable gets hoisted

全球let没有得到重视window

与在全球范围内let声明的变量(它是代码,是不是在一个功能)没有得到添加作为全球window对象上的属性。例如(该代码是在全局范围内):

var bar = 5;
let foo  = 10;

console.log(bar); // logs 5
console.log(foo); // logs 10

console.log(window.bar);  
// logs 5, variable added to window object

console.log(window.foo);
// logs undefined, variable not added to window object

结果

  

:当应let超过var使用吗

使用过let var每当你可以,因为它只是范围的更具体。这减少了大量的变量打交道时,可能出现潜在的命名冲突。 var可以用来当你想要一个全局变量明确是window对象(总是仔细考虑,如果这是真的有必要)。

还可以看出,至少在Visual Studio 2015,打字稿1.5,“变种”允许在一个块中的相同的变量名的多个声明,和“让”不

这将不会产生编译错误:

var x = 1;
var x = 2;

此意愿:

let x = 1;
let x = 2;

var是全局范围(葫芦能)变量。

letconst块范围。

  

test.js

{
    let l = 'let';
    const c = 'const';
    var v = 'var';
    v2 = 'var 2';
}

console.log(v, this.v);
console.log(v2, this.v2);
console.log(l); // ReferenceError: l is not defined
console.log(c); // ReferenceError: c is not defined

let

let关键字附加的变量声明到任何块(通常是{ .. }对)的范围它包含在。换句话说,let隐含劫持块的其变量声明范围。

let变量不能在window对象访问,因为它们不能被全局访问。

function a(){
    { // this is the Max Scope for let variable
        let x = 12;
    }
    console.log(x);
}
a(); // Uncaught ReferenceError: x is not defined

var

在功能意味着变量

var和变量ES5具有范围是函数本身外的函数内,并且不是有效的。

var变量可以在window对象访问,因为它们不能被全局访问。

function a(){ // this is the Max Scope for var variable
    { 
        var x = 12;
    }
    console.log(x);
}
a(); // 12

如果您想了解更多继续阅读下面的

的范围上也可以足够了确切使用let并且如下var最著名的面试问题之一;

:当使用let

for (let i = 0; i < 10 ; i++) {
    setTimeout(
        function a() {
            console.log(i); //print 0 to 9, that is literally AWW!!!
        }, 
        100 * i);
}

这是因为使用时let,对于每一个循环迭代变量的作用域确定和有它自己的副本。

:当使用var

for (var i = 0; i < 10 ; i++) {
    setTimeout(
        function a() {
            console.log(i); //print 10 times 10
        }, 
        100 * i);
}

这是因为使用时var,对于每一个循环迭代变量的作用域确定并已共享拷贝。

如果我当时阅读了规格 let 谢天谢地 也可以利用杠杆来避免 自调用函数 用于模拟仅限私人成员 - 一种流行的设计模式,它会降低代码的可读性,使调试复杂化,不会增加真正的代码保护或其他好处 - 除了可能满足某人对语义的渴望,所以停止使用它。/咆哮

var SomeConstructor;

{
    let privateScope = {};

    SomeConstructor = function SomeConstructor () {
        this.someProperty = "foo";
        privateScope.hiddenProperty = "bar";
    }

    SomeConstructor.prototype.showPublic = function () {
        console.log(this.someProperty); // foo
    }

    SomeConstructor.prototype.showPrivate = function () {
        console.log(privateScope.hiddenProperty); // bar
    }

}

var myInstance = new SomeConstructor();

myInstance.showPublic();
myInstance.showPrivate();

console.log(privateScope.hiddenProperty); // error

看 '模拟私有接口'

let一些黑客:

1

    let statistics = [16, 170, 10];
    let [age, height, grade] = statistics;

    console.log(height)

2

    let x = 120,
    y = 12;
    [x, y] = [y, x];
    console.log(`x: ${x} y: ${y}`);

3

    let node = {
                   type: "Identifier",
                   name: "foo"
               };

    let { type, name, value } = node;

    console.log(type);      // "Identifier"
    console.log(name);      // "foo"
    console.log(value);     // undefined

    let node = {
        type: "Identifier"
    };

    let { type: localType, name: localName = "bar" } = node;

    console.log(localType);     // "Identifier"
    console.log(localName);     // "bar"

Getter和与let设定器:

let jar = {
    numberOfCookies: 10,
    get cookies() {
        return this.numberOfCookies;
    },
    set cookies(value) {
        this.numberOfCookies = value;
    }
};

console.log(jar.cookies)
jar.cookies = 7;

console.log(jar.cookies)

让VS变种。这是所有关于范围即可。

var变量的值是全球并基本上可以随处访问,而让变量都不是全局的并只存在,直到一个右括号杀死他们。

请参阅我的例子下面,并注意如何狮子(LET)变量的行为不同的方式在两个console.logs;它变成超出范围的第二的console.log。

var cat = "cat";
let dog = "dog";

var animals = () => {
    var giraffe = "giraffe";
    let lion = "lion";

    console.log(cat);  //will print 'cat'.
    console.log(dog);  //will print 'dog', because dog was declared outside this function (like var cat).

    console.log(giraffe); //will print 'giraffe'.
    console.log(lion); //will print 'lion', as lion is within scope.
}

console.log(giraffe); //will print 'giraffe', as giraffe is a global variable (var).
console.log(lion); //will print UNDEFINED, as lion is a 'let' variable and is now out of scope.

让是ES6的一部分。这些功能将在解释简单的方法的差异。

function varTest() {
  var x = 1;
  if (true) {
    var x = 2;  // same variable!
    console.log(x);  // 2
  }
  console.log(x);  // 2
}

function letTest() {
  let x = 1;
  if (true) {
    let x = 2;  // different variable
    console.log(x);  // 2
  }
  console.log(x);  // 1
}

以前 JavaScript 中只有两个作用域,即功能性和全球性。和 'let' 关键字 JavaScript 现已引入 block-level 变量。

要完全理解“let”关键字, ES6:JavaScript 中使用“let”关键字声明变量 会有帮助的。

现在我认为有变量更好作用域使用let语句块:

function printnums()
{
    // i is not accessible here
    for(let i = 0; i <10; i+=)
    {
       console.log(i);
    }
    // i is not accessible here

    // j is accessible here
    for(var j = 0; j <10; j++)
    {
       console.log(j);
    }
    // j is accessible here
}

我认为人们会开始使用让这里经过,使他们有类似的作用域在JavaScript和其它语言一样,Java和C#等。

有关在JavaScript范围界定不清晰的认识,人们习惯犯错误早。

起重没有使用let支持。

使用本JavaScript中这种方法的误差得到去除。

参考 ES6在深度:让和const 更好地理解它

这篇文章明确定义了var、let和const之间的区别

const 是标识符不会被重新分配的信号。

let, ,是可以重新赋值变量的信号,例如 循环中的计数器,或算法中的值交换。它还发出信号 该变量将仅在定义它的块中使用, 这并不总是整个包含函数。

var 现在是定义变量时可用的最弱信号 在 JavaScript 中。变量可以重新赋值,也可以不赋值,并且 变量可以用于也可以不用于整个函数,或者仅用于 块或循环的用途。

https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75#.esmkpbg9b

如上所述:

  

的差被确定范围。 var可以被限制在最近的功能   块let的作用范围的最近的封闭块下,其   可以比功能块小。无论是全球性的,如果外部的任何   block.Lets看到的示例:

<强>例1:

在我的这两个例子中我有一个函数myfuncmyfunc含有可变myvar等于10。 在我的第一个例子中,我检查,如果myvar等于10(myvar==10)。如果是的话,我agian声明一个变量myvar使用var关键字(现在我有两个MYVAR变量),并为其分配一个新的值(20)。在下一行打印我的控制台上的价值。条件块后,我再次打印我的控制台上myvar的价值。如果看一下myfunc的输出,myvar具有值等于20

“让关键词”

<强>例2: 在我的第二个例子,而不是在我的条件块使用var关键字我使用myvar关键字声明let。现在,当我打电话myfunc我得到两个不同的输出:myvar=20myvar=10

因此,不同的是即它的范围非常简单。

“在这里输入的图像描述”

看看这个图像,我创建用于constlet变量的示范一个非常简单的例子。正如你所看到的,当你试图改变const变量,你会得到错误(试图重写“名字”,这是不变的“),但看看let变量...

首先声明let age = 33,后来分配一些其它值age = 34;,这是确定,我们没有任何错误,当我们试图改变let变量

我认为术语和大部分的例子是比较难,但 主要的问题我与差异亲自过是理解什么是“块”。 在某些时候,我意识到,一个块是除了IF声明任何大括号。 函数或循环的开口托架{将限定一个新的块,具有在其内限定let任何东西,不会是同一件事右括号}(功能或循环)后可用; 考虑到这一点,这是比较容易理解的:

let msg = "Hello World";

function doWork() { // msg will be available since it was defined above this opening bracket!
  let friends = 0;
  console.log(msg);

  // with VAR though:
  for (var iCount2 = 0; iCount2 < 5; iCount2++) {} // iCount2 will be available after this closing bracket!
  console.log(iCount2);
  
    for (let iCount1 = 0; iCount1 < 5; iCount1++) {} // iCount1 will not be available behind this closing bracket, it will return undefined
  console.log(iCount1);
  
} // friends will no be available after this closing bracket!
doWork();
console.log(friends);

由于目前我正在试图获得一个在JavaScript的深入了解,我将分享我的其中包含一些已经讨论过的伟大的作品加上其他一些细节在不同的角度简要研究。

了解的 VAR 可让如果我们明白之间的差异可以更容易的功能块范围

让我们考虑以下情况:

(function timer() {
    for(var i = 0; i <= 5; i++) {
        setTimeout(function notime() { console.log(i); }, i * 1000);
    }
})();


   Stack            VariableEnvironment //one VariablEnvironment for timer();
                                       // when the timer is out - the value will be the same value for each call
5. [setTimeout, i]  [i=5] 
4. [setTimeout, i]  
3. [setTimeout, i]
2. [setTimeout, i]
1. [setTimeout, i]
0. [setTimeout, i]

####################    

(function timer() {
    for (let i = 0; i <= 5; i++) {
        setTimeout(function notime() { console.log(i); }, i * 1000);
    }
})();

   Stack           LexicalEnvironment - each iteration has a new lexical environment
5. [setTimeout, i]  [i=5]       
                      LexicalEnvironment 
4. [setTimeout, i]    [i=4]     
                        LexicalEnvironment 
3. [setTimeout, i]      [i=3]       
                         LexicalEnvironment 
2. [setTimeout, i]       [i=2]
                           LexicalEnvironment 
1. [setTimeout, i]         [i=1]
                             LexicalEnvironment 
0. [setTimeout, i]           [i=0]

timer()被调用一个的的ExecutionContext 创建这将同时包含在 VariableEnvironment 并所有的 LexicalEnvironments 对应于每次迭代

和一个简单的例子

功能范围

function test() {
    for(var z = 0; z < 69; z++) {
        //todo
    }
    //z is visible outside the loop
}

块范围

function test() {
    for(let z = 0; z < 69; z++) {
        //todo
    }
    //z is not defined :(
}

我想这些关键字链接到执行上下文,因为执行上下文是在这一切的重要。执行上下文有两个阶段:创建阶段和执行阶段。此外,每个执行上下文具有可变的环境与外部环境(其词法环境)。

期间的执行上下文的创建阶段,无功,让和const仍将其在存储器中存储的变量在给定的执行上下文的可变环境未定义的值。所不同的是在执行阶段。如果您使用的参考它分配一个值之前使用var定义的变量,它将是不确定的。不将引发异常。

然而,不能,直到它被声明引用与Let或const声明的变量。如果您尝试使用它宣告前,然后一个异常将被执行上下文的执行阶段时提出的。现在变量仍然会在内存中,执行上下文的创建阶段的礼貌,但发动机不会让你使用它:

function a(){
    b;
    let b;
}
a();
> Uncaught ReferenceError: b is not defined

通过使用var定义,如果发动机不能找到在当前执行上下文的可变环境的变量,那么它会上升的范围链(外环境),并检查外部环境的可变环境变量的变量。如果无法找到它,它会继续搜索作用域链。这不是让我们和const的情况。

让的第二个特征是它引入了块范围。块由大括号限定。实例包括功能块,如果块,块等。当声明变量与设A块的内,该变量是唯一的块的内部提供。事实上,每次块运行时,如在for循环内,将在存储器中创建一个新的变量。

ES6还引入了用于声明变量const关键字。 const为还阻断作用域。令和const之间的区别是,常量变量需要使用一个初始化被声明,否则会产生错误。

最后,当涉及到的执行上下文,使用var定义的变量将被附连到“这个”对象。在全局执行环境,这将是在浏览器窗口对象。这不是为让或const的情况。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top