提问



ECMAScript 6引入了let语句。我听说它被描述为本地变量,但我仍然不太确定它与var关键字的行为有何不同。[198]


有什么区别?何时let应该在var上使用?

最佳参考


区别在于范围界定。 var的范围限定为最近的功能块,let的范围限定为最近的封闭块,该块可能小于功能块。如果在任何区域之外,两者都是全球


此外,使用let声明的变量在它们的封闭块中声明之前是不可访问的。如演示中所示,这将引发ReferenceError异常。


演示: [199]




var html = '';

write('#### global ####\n');
write('globalVar: ' + globalVar); //undefined, but visible

try {
  write('globalLet: ' + globalLet); //undefined, *not* visible
} catch (exception) {
  write('globalLet: exception');
}

write('\nset variables');

var globalVar = 'globalVar';
let globalLet = 'globalLet';

write('\nglobalVar: ' + globalVar);
write('globalLet: ' + globalLet);

function functionScoped() {
  write('\n#### function ####');
  write('\nfunctionVar: ' + functionVar); //undefined, but visible

  try {
    write('functionLet: ' + functionLet); //undefined, *not* visible
  } catch (exception) {
    write('functionLet: exception');
  }

  write('\nset variables');

  var functionVar = 'functionVar';
  let functionLet = 'functionLet';

  write('\nfunctionVar: ' + functionVar);
  write('functionLet: ' + functionLet);
}

function blockScoped() {
  write('\n#### block ####');
  write('\nblockVar: ' + blockVar); //undefined, but visible

  try {
    write('blockLet: ' + blockLet); //undefined, *not* visible
  } catch (exception) {
    write('blockLet: exception');
  }

  for (var blockVar = 'blockVar', blockIndex = 0; blockIndex < 1; blockIndex++) {
    write('\nblockVar: ' + blockVar); // visible here and whole function
  };

  for (let blockLet = 'blockLet', letIndex = 0; letIndex < 1; letIndex++) {
    write('blockLet: ' + blockLet); // visible only here
  };

  write('\nblockVar: ' + blockVar);

  try {
    write('blockLet: ' + blockLet); //undefined, *not* visible
  } catch (exception) {
    write('blockLet: exception');
  }
}

function write(line) {
  html += (line ? line : '') + '<br />';
}

functionScoped();
blockScoped();

document.getElementById('results').innerHTML = html;

<pre id="results"></pre>



let也可用于避免闭包问题。它绑定了新的价值,而不是保留旧的参考,如下面的例子所示。


样本 [200]


for(var i = 1; i < 6; i++) {
  document.getElementById('my-element' + i)
    .addEventListener('click', function() { alert(i) })
}


上面的代码演示了一个典型的JavaScript闭包问题对i变量的引用存储在单击处理程序闭包中,而不是i的实际值。


每个单击处理程序都将引用同一个对象,因为只有一个计数器对象可以容纳6,因此每次单击时会得到6个。


一般的解决方法是将它包装在一个匿名函数中并传递i作为参数。现在也可以通过使用let而不是var来避免这些问题,如下面的代码所示。


DEMO (在Chrome和Firefox 50中测试过)[201]


'use strict';

for(let i = 1; i < 6; i++) {
  document.getElementById('my-element' + i)
    .addEventListener('click', function() { alert(i) })
}

其它参考1


这里用let关键字解释了一些例子。[202]



  让我的工作非常像var。主要区别在于var变量的范围是整个封闭函数



维基百科上的这个表显示了哪些浏览器支持Javascript 1.7。[203]


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

其它参考2


letvar之间有什么区别?




  • 使用var语句定义的变量在函数开头的函数中是已知的。 (*)

  • 使用let语句定义的变量仅在定义的中已知,从定义之后开始。 (**)



要了解差异,请考虑以下代码:[204] [205]


// 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语句将会过时.java guru Kyle Simpson 写了一篇非常精细的文章,说明为什么不是这样的即可。[206] [207]


然而,今天绝对不是这样。事实上,我们实际上需要问自己,使用let语句是否安全。这个问题的答案取决于你的环境:



  • 如果您正在编写服务器端JavaScript代码( Node.js ),则可以安全地使用let语句。[208]

  • 如果您正在编写客户端JavaScript代码并使用转换器(如 Traceur ),您可以安全地使用let语句,但是您的代码很可能只是在性能方面最佳。[209]

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



今天,2018年6月8日,仍然有一些浏览器不支持let!


[210]





如何跟踪浏览器支持



有关哪些浏览器在您阅读此答案时支持let语句的最新概述,请参阅Can I Use[211]





(*)全局和功能范围的变量可以在声明它们之前进行初始化和使用,因为JavaScript变量是悬挂。这意味着声明始终位于范围的顶部[212]


(**)未提升块范围变量

其它参考3


接受的答案缺少一点:


{
  let a = 123;
};

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

其它参考4


存在一些微妙的差异 - let范围表现更像是变量范围在或多或少的任何其他语言。


例如它适用于封闭的块,它们在它们被宣布之前不存在,等等。


然而值得注意的是let只是新Javascript实现的一部分,并且具有不同程度的浏览器支持。[213]

其它参考5


以下是两者之间差异的示例(支持刚启动的chrome):



如您所见,var j变量仍然具有for循环范围之外的值(块范围),但[[for循环范围外]]的let i变量未定义。




"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



阻止范围



使用let关键字声明的变量是块范围的,这意味着它们仅在声明它们的块中可用。[214]


在顶层(在功能之外)



在顶层,使用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


在函数内部



在函数内部(但在块之外),letvar具有相同的范围。


(() => {
  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声明的变量只能在该循环内引用。


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



constlet非常相似 - 它是块范围的并且具有TDZ。但是,有两个不同的东西。


无需重新分配



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


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


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


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


如果你想拥有一个不可变对象,你应该使用Object.freeze()[216]


需要初始化程序



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


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

其它参考6



  • 变量不提升


    let不提升到它们出现的块的整个范围。相比之下,var可以如下所示提升。


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


    实际上,Per @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);  
    }
    


    但是,用let替换var


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


    因为let创建一个新的词汇环境,其中包含a)初始化表达式b)每次迭代(主要用于评估增量表达式),更多细节在这里。


其它参考7


主要区别在于范围差异,而 let 只能在声明的范围内使用,例如for循环, var 可以例如,从MDN中的文档(MDN中的示例):[219]



  允许您将范围有限的变量声明为使用它的块,语句或表达式。这与 var 关键字不同, var 关键字全局定义变量,或者与整个函数本地定义,而不管块范围如何。

  
  由 let 声明的变量的范围是定义它们的块,以及任何包含的子块。通过这种方式,非常像 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功能,所以它还没有完全支持,所以最好总是将它转发给使用Babel等的ECMA5 ......有关访问babel网站[220]的更多信息

其它参考8


这是一个添加其他人已经写过的例子。假设你想要创建一个函数数组,adderFunctions,其中每个函数都接受一个Number参数并返回参数和函数的总和数组中的索引。尝试使用var关键字循环生成adderFunctions不会像某人可能天真期望的那样工作:


// 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块的迭代。相反,在循环结束时,每个函数的闭包中的iadderFunctions中的每个匿名函数的循环结束时引用i的值(1000) 。这根本不是我们想要的:我们现在在内存中有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


这次,ifor循环的每次迭代中反弹。现在,每个函数在函数创建时保持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行为以及函数闭包,这些行为也很容易理解。这将是添加数字的可怕方式。但是在其他环境中的现实世界中可能会遇到在匿名函数闭包中捕获数据的一般技术。因人而异。


其它参考9


区别在于每个变量的变量范围。[221]


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



  1. let变量仅在最近的封闭块中可见({ ... })。

  2. let变量只能在声明变量之后出现的代码行中使用(即使它们被挂起!)。

  3. let变量不得由后续varlet重新声明。

  4. 全局let变量未添加到全局window对象。

  5. let变量易于使用带闭包(它们不会导致竞争条件)。



let施加的限制降低了变量的可见性,并增加了早期发现意外名称冲突的可能性。这样可以更容易地跟踪和推理变量,包括它们的可达性(帮助回收未使用的内存)。/questions/33198849 [223] [224]


因此,let变量在大型程序中使用时或者以独立开发的框架以新的和意外的方式组合时不太可能导致问题。


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


实施例



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。 Globals与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*/);
}


...循环意外地输出我是5五次:


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

其它参考10


可能以下两个函数显示了不同之处:


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
}

其它参考11


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]]。

其它参考12


看来,至少在Visual Studio 2015中,TypeScript 1.5,var允许在块中对同一个变量名进行多次声明,而let则不允许。


这不会产生编译错误:


var x = 1;
var x = 2;


这会:


let x = 1;
let x = 2;

其它参考13


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 谢天谢地也可以用来避免用于模拟私有成员的自调用函数 - 一种降低代码可读性的流行设计模式,使调试复杂化,这不会增加真正的代码保护或其他好处 - 除了可能满足某人对语义的渴望,所以停止使用它./rant


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


参见模拟私有接口[226]

其它参考14


一些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和setter 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)

其它参考15


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


如果您想了解更多信息,请继续阅读


关于范围的最着名的访谈问题之一也可以满足letvar的确切用法,如下所示;


使用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时,对于每个循环迭代,变量都是作用域并具有共享副本。

其它参考16


let是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
}

其它参考17


以前在JavaScript中只有两个范围,即功能范围和全局范围。使用let关键字JavaScript现在引入了block-level个变量。


要完全理解let关键字
ES6:'let'关键字在JavaScript中声明变量会有所帮助。[227]

其它参考18


现在我认为使用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
}


我认为人们将在这之后开始使用let,以便他们在JavaScript中具有类似的范围,如其他语言,Java,C#等。


那些对JavaScript中的作用域不太了解的人过去常犯错误。


使用let不支持提升。


使用此方法,JavaScript中出现的错误将被删除。


请参阅 ES6 In Depth:let和const 以更好地理解它。[228]

其它参考19


本文明确定义了var,let和const之间的区别



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

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

  
  var现在是定义变量时可用的最弱信号
  在JavaScript中。变量可能会也可能不会被重新分配,而且
  变量可以用于也可以不用于整个函数,或仅用于
  块或循环的目的。



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

其它参考20


ECMAScript 6增加了一个关键字来声明除let以外的const变量。[230]


在var上引入let和const的主要目的是使用块方法而不是传统的词法范围。
本文非常简要地解释了var和let之间的区别,它还涵盖了对const的讨论。[231]

其它参考21


检查此链接
MDN [232]


let x = 1;

if (x === 1) {
let x = 2;

console.log(x);
// expected output: 2
}

console.log(x);
// expected output: 1