32 题: 使用“let”和“var”之间有什么区别?

在...创建的问题 Mon, Mar 18, 2019 12:00 AM

ECMAScript 6介绍了 let声明

我听说它被描述为“本地”变量,但我仍然不太确定它与var关键字的行为有何不同。

有什么区别? let何时应该超过var使用?

    
3910
  1. ECMAScript是标准,let包含在第6版草案并且很可能在最终规范中。
    2012-03-31 15:08:35Z
  2. 请参阅 kangax.github .io /es5-compat-table /es6 获取ES6功能的最新支持矩阵(包括let)。在撰写Firefox时,Chrome和IE11都支持它(虽然我认为FF的实现不是很标准)。
    2014-01-17 12:37:00Z
  3. 在最长的时间里,我不知道for循环中的变量被限定在它所包含的函数中。我记得第一次想出这个并且想到了这是非常愚蠢的。我确实看到了一些力量,虽然现在知道如何使用这两个因为不同的原因以及在某些情况下你可能真的想在for循环中使用var并且没有将它限定为块。
    2015-05-07 13:54:27Z
  4. 随着ES6功能支持的改进,有关ES6采用的问题将焦点从功能支持转移到性能差异。因此,这是我发现ES6和ES5之间基准性能差异的网站。请记住,随着引擎优化ES6代码,这可能会随着时间的推移而发生变化。
    2016-05-04 01:02:26Z
  5. 这是一个非常好的阅读 wesbos.com/JavaScript的作用域
    2017-06-15 09:32:01Z
  6. 醇>
    30答案                              30 跨度>                         

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

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

    演示

     
    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>

    全局:

    在功能块之外使用它们非常相似。

     
    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 me = 'go';  // globally scoped
    var i = 'able'; // globally scoped
    
    对象上,就像let定义的那样。  window

    功能:

    在功能块中使用时,它们是相同的。

     var

    块:

    这是区别。

    console.log(window.me); // undefined
    console.log(window.i); // 'able'
    
    仅在
    function ingWithinEstablishedParameters() {
        let terOfRecommendation = 'awesome worker!'; //function block scoped
        var sityCheerleading = 'go!'; //function block scoped
    }
    
    循环中可见,let对整个函数可见。  for()

    重新说明:

    假设严格模式,var将允许您在同一范围内重新声明相同的变量。

     
    function allyIlliterate() {
        //tuce is *not* visible out here
    
        for( let tuce = 0; tuce < 5; tuce++ ) {
            //tuce is only visible in here (and in the for() parentheses)
            //and there is a separate tuce variable for each iteration of the loop
        }
    
        //tuce is *not* visible out here
    }
    
    function byE40() {
        //nish *is* visible out here
    
        for( var nish = 0; nish < 5; nish++ ) {
            //nish is visible to the whole function
        }
    
        //nish *is* visible out here
    }
    

    另一方面,var不会:

     
    'use strict';
    var me = 'foo';
    var me = 'bar'; // No problem, 'me' is replaced.
    
        
    5325
    2019-06-24 02:46:57Z
    1. 请记住,您可以随时创建块。 function(){code; {let inBlock = 5;代码; };
      2012-12-14 10:14:27Z
    2. 那么let语句的目的是在某个块中不需要时释放内存吗?
      2013-06-07 05:18:58Z
    3. @ NoBugs,是的,鼓励变量仅在需要的地方存在。
      2013-06-07 15:02:29Z
    4. let块表达式
      'use strict';
      let me = 'foo';
      let me = 'bar'; // SyntaxError: Identifier 'me' has already been declared
      
      是非标准的,将来会被删除, bugzilla.mozilla.org/show_bug.cgi?id=1023609
      2014-12-17 14:51:12Z
    5. 所以,我只是想不出任何使用var的情况。有人能给我一个例子,说明最好使用var吗?
      2015-11-08 13:12:08Z
    6. 醇>

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

    演示

     let (variable declaration) statement

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

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

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

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

    演示 (在Chrome和Firefox 50中测试)

     let     
    531
    2019-06-24 02:47:37Z
    1. 这实际上很酷。我希望在循环体外面定义“i”包含在括号内,而不是在“i”周围形成“闭包”。当然,你的例子证明不是这样。从语法的角度来看,我认为这有点令人困惑,但这种情况非常普遍,以这种方式支持它是有道理的。非常感谢你提出这个问题。
      2015-07-27 12:49:33Z
    2. IE 11支持var,但它为所有按钮发出“6”警告。你有任何消息来源说
      'use strict';
      
      for(let i = 1; i < 6; i++) {
        document.getElementById('my-element' + i)
          .addEventListener('click', function() { alert(i) })
      }
      
      应该如何表现吗?
      2015-10-22 13:29:07Z
    3. 看起来你的答案是正确的行为: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
      2015-10-22 13:32:39Z
    4. 确实这是Javascript中常见的陷阱,现在我可以看到为什么let会非常有用。在循环中设置事件侦听器不再需要立即调用的函数表达式,以便在每次迭代时对本地作用域let进行调整。
      2016-02-21 08:12:08Z
    5. 使用“let”只是推迟了这个问题。因此每次迭代都会创建一个私有的独立块作用域,但是“i”变量仍然可能被块内的后续更改破坏,(授予迭代器变量通常在块内更改,但其他声明让块中的变量很可能是)并且在块中声明的任何函数在被调用时都会破坏其他f的“i”值在块中声明的unctions因为它们共享相同的私有块作用域,因此对“i”的引用相同。
      2016-09-07 23:10:20Z
    6. 醇>

    leti有什么区别?

    • 使用let语句定义的变量在 函数 ,从函数的开头定义。 *
    • 使用var语句定义的变量仅在中为人所知。 ,从定义之后开始定义。 **

    要了解其中的差异,请考虑以下代码:

     var

    在这里,我们可以看到我们的变量let仅在第一个for循环中已知,但在之前和之后都不知道。然而,我们的变量

    // 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是否安全?

    有些人会争辩说,将来我们只会使用let语句,而var语句将会过时。 JavaScript大师 Kyle Simpson 写了 一篇非常精细的文章,说明为什么他认为情况并非如此

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


    如何跟踪浏览器支持

    有关在阅读本答案时哪些浏览器支持let语句的最新概述,请参阅 let


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

    ** 未提升块范围变量

        
    144
    2019-06-24 02:50:20Z
    1. 关于答案v4:let在功能块中到处都知道!它以Can I Use(由于提升)开始,直到你分配一个值!ps:i也被悬挂(在它的顶部包含块),但在第一次分配之前在块中引用时会给出一个undefined。(ps2:我是一个支持分号的人,但你真的不需要在一个块之后用分号)。这就是说,谢谢你加入关于支持的现实检查!
      2016-05-21 04:41:11Z
    2. @ GitaarLAB:根据 Mozilla开发者网络:”在ECMAScript 2015中,让绑定不受变量提升的影响,这意味着让声明不会移动到当前执行上下文的顶部。“ - 无论如何,我对我的答案做了一些改进,应该澄清letReferenceError之间提升行为的区别!
      2018-02-26 23:37:27Z
    3. 你的答案得到了很大改善(我彻底检查过)。请注意,您在评论中引用的相同链接也会说:“(let)变量位于块开始的”临时死区“,直到处理完初始化为止。这意味着'标识符'(文本字符串'保留'指向'某事物')已经在相关范围内保留,否则它将成为根/主机/窗口范围的一部分。就我个人而言,“吊装”仅仅意味着将声明的“标识符”保留/链接到相关范围;排除其初始化/赋值/可修改性!
      2018-03-01 18:16:42Z
    4. 和.. + 1。您链接的Kyle Simpson文章是优秀的阅读,谢谢你!关于“时间死区”又名“TDZ”也很清楚。我想补充一件有趣的事情:我已经在MDN上看到,letvar 建议仅在您真正需要其他功能时才使用,因为强制执行/检查这些额外功能(如写入) -only const)导致(当前)引擎执行/检查/验证/设置的“更多工作”(以及范围树中的其他范围 - 节点)。
      2018-03-01 18:17:20Z
    5. 请注意,MDN说IE DOES正确解释。这是什么? developer.mozilla.org/en-US/docs/网络/JavaScript的/参考/...
      2019-02-06 12:42:50Z
    6. 醇>

    以下是一些let关键字的解释以及一些示例。

      const非常像let。主要区别在于let变量的范围是整个封闭函数

    维基百科上的此表显示哪些浏览器支持Javascript 1.7。

    请注意,只有Mozilla和Chrome浏览器支持它。 IE,Safari和其他可能没有。

        
    139
    2019-06-24 02:52:39Z
    1. 来自链接文档的文本的关键位似乎是“让我们非常像var。主要区别在于var变量的范围是整个封闭功能“。
      2009-04-17 20:25:25Z
    2. 虽然说IE不支持它在技术上是正确的,但说它只是一个mozilla扩展更为正确。
      2009-04-17 22:56:39Z
    3. @ olliej,实际上是Mozilla是ju在比赛前面。参见 ecma-international.org/publications/的第19页文件/ECMA-ST /ECMA-262.pdf
      2012-06-18 20:16:29Z
    4. @ TylerCrompton,这只是多年来保留的单词集。当mozilla添加时,让它纯粹是一个mozilla扩展,没有相关的规范。 ES6应该定义let语句的行为,但是在mozilla引入语法之后。记住moz也有E4X,它完全是死的而且只是moz。
      2012-07-11 18:49:45Z
    5. 2013-12-24 12:59:15Z
    6. 醇>

    接受的答案缺少一点:

     var     
    106
    2016-07-14 14:13:44Z
    1. 接受的答案解释了这一点。
      2015-06-04 20:24:15Z
    2. 接受的答案并未在其示例中解释这一点。接受的答案仅在let循环初始化程序中证明了这一点,大大缩小了
      {
        let a = 123;
      };
      
      console.log(a); // ReferenceError: a is not defined
      
      限制的应用范围。 Upvoted。
      2015-09-22 06:55:21Z
    3. @ stimpy77它明确指出“let is scoped to the nearest enclosing block”;是否需要包含所有表现形式?
      2016-03-31 21:32:13Z
    4. 有很多例子,没有一个能够正确地证明这个问题。我可能已经推荐了接受的答案和这个答案吗?
      2016-03-31 21:38:33Z
    5. 此贡献表明“块”可以简单地用括号括起来的一组行;即它不需要与任何类型的控制流,循环等相关联。
      2017-11-22 14:37:22Z
    6. 醇>

    for

    阻止范围

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

    在顶层(在功能之外)

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

    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 -it的块范围并且具有TDZ。然而,有两件事是不同的。

    无需重新分配

    使用const声明的变量无法重新分配。

     let

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

     const

    如果你想拥有一个不可变对象,你应该使用

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

    需要初始化程序

    使用

    const obj = {};
    obj.a = 42;
    console.log(obj.a); // 42
    
    声明变量时,必须始终指定一个值。  Object.freeze()     
    63
    2018-10-25 20:54:47Z

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

    正如您所看到的,const变量的值仍然在for循环范围之外(Block Scope),但

    const a; // SyntaxError: Missing initializer in const declaration
    
    变量在for循环范围之外未定义。

     var j
        
    44
    2019-06-05 12:03:19Z
    1. 我在这看什么工具?
      2015-03-24 21:43:48Z
    2. Chrome devtools
      2015-03-24 21:44:52Z
    3. 作为Cinnamon桌面小程序的开发者,我没有接触过这样的闪亮工具。
      2017-10-26 04:20:30Z
    4. 醇>

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

    e.g。它适用于封闭块,它们在声明之前不存在等等。

    然而值得注意的是,

    "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);
    只是较新的Javascript实现的一部分,并且具有不同程度的浏览器支持     
    44
    2019-06-24 02:53:08Z
    1. 值得注意的是,ECMAScript是标准,let包含在第6版草案,很可能会出现在最终规范中。
      2012-03-31 15:09:58Z
    2. 这就是3年的差异:D
      2012-04-13 03:28:43Z
    3. 刚刚对这个问题进行了调查,2012年仍然只有Mozilla浏览器支持let。 Safari,IE和Chome都没有。
      2012-07-13 17:38:18Z
    4. 在事故中意外创建部分块范围的想法是一个好点,请注意,let不提升,使用由顶部定义的let定义的变量你的街区。如果您的let语句不仅仅是几行代码,您可能会忘记在定义之后才能使用该变量。伟大的要点!!!
      2015-05-07 14:01:33Z
    5. @ EricB:是和否:“在ECMAScript 2015中,let 变量提升到块的顶部。但是,参考变量声明之前块中的变量导致 ReferenceError (我的注释:而不是旧的if)。变量处于从块开始到处理声明的“临时死区”中。“切换语句也是如此,因为只有一个底层块”。来源: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
      2016-05-21 04:15:53Z
    6. 醇>
    • 变量未挂起

      let 不会提升到它们出现的整个范围。相比之下,undefined可以升降如下。

       let

      实际上,Per @Bergi,两者var

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

    • 垃圾收集

      var的块范围与闭包和垃圾回收有关,用于回收内存。考虑,

       let

      let处理程序回调根本不需要

      function process(data) {
          //...
      }
      
      var hugeData = { .. };
      
      process(hugeData);
      
      var btn = document.getElementById("mybutton");
      btn.addEventListener( "click", function click(evt){
          //....
      });
      
      变量。理论上,在click运行之后,巨大的数据结构hugeData可能被垃圾收集。但是,有些JS引擎仍然需要保留这个庞大的结构,因为process(..)函数在整个范围内都有一个闭包。

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

       hugeData
    • 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);  
      }
      
       var

      因为let使用这些名称创建了一个新的词法环境,a)初始化表达式b)每次迭代(主要用于评估增量表达式),更多细节是此处

    21
    2017-05-23 12:34:53Z
    1. 2016-12-02 00:24:57Z
    2. 他们被悬挂,但由于(鼓滚)时间死区而表现得好像没有悬挂 - 这是一个非常引人注目的名称,因为标识符在宣布之前无法访问: - )
      2016-12-31 15:42:05Z
    3. 所以让我们悬挂,但不可用?这与“没有悬挂”有什么不同?
      2017-11-21 21:46:30Z
    4. 希望Brian或Bergi回来回答这个问题。是悬挂的声明,但不是作业?谢谢!
      2017-11-22 16:18:57Z
    5. @ N-ate,这是一个帖子,也许你可以在里面找到答案。
      2017-11-23 03:46:46Z
    6. 醇>

    主要区别在于范围差异,而只能在声明的范围中使用,例如for for循环, var 。来自 MDN 中的文档(示例也是来自MDN):

      

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

         

    let 声明的变量的范围是定义它们的块,以及任何包含的子块。通过这种方式,非常像 var 。主要区别在于 var 变量的范围是整个封闭函数:

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

    在程序和功能的顶层, var 不同,不会在全局对象上创建属性。例如:

     let
      

    在块内使用时,将变量的范围限制为该块。注意 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
    }`
    

    另外不要忘记它的ECMA6功能,所以它还没有完全支持,所以最好总是使用Babel等将它转换为ECMA5 ...有关访问的更多信息 babel网站

        
    21
    2019-01-18 07:03:40Z

    这是一个添加其他人已经写过的例子。假设您要创建一个函数数组

    var x = 'global';
    let y = 'global';
    console.log(this.x); // "global"
    console.log(this.y); // undefined
    
    ,其中每个函数都使用一个Number参数,并返回参数和函数索引在数组中的总和。尝试使用
    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
    
    关键字生成带有循环的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中每个匿名函数在循环结束时的值(1000)。这根本不是我们想要的:我们现在在内存中有一个包含1000个不同函数的数组,具有完全相同的行为。如果我们随后更新i的值,则突变将影响所有i

    但是,我们可以使用adderFunctions关键字重试:

     i

    这一次,adderFunctionslet循环的每次迭代中都会反弹。现在,每个函数在创建函数时都保持

    // 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的行为与预期的一样。

    现在,图像混合了两种行为,你可能会明白为什么不建议在同一个脚本中将较新的fori与较旧的adderFunctions混合使用。这样做会导致一些令人费解的代码。

     let

    不要让这件事发生在你身上。使用linter。

      

    注意:这是一个教学示例,旨在演示循环中的const/var行为以及功能闭包,这也很容易理解。这将是添加数字的可怕方式。但是在匿名函数闭包中捕获数据的一般技术可能在其他环境中的现实世界中遇到。 YMMV。

        
    15
    2017-10-09 22:24:59Z
    1. @ aborz:在第二个例子中也是非常酷的匿名函数语法。这正是我在C#中习惯的。我今天学到了一些东西。
      2015-02-20 08:59:24Z
    2. 更正:从技术上讲,此处描述的箭头函数语法=&gt; developer.mozilla.org/en-US/docs/网络/JavaScript的/参考/...
      2015-03-16 06:58:17Z
    3. 实际上,你不需要
      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语句创建了一个词法块。
      2015-10-22 22:38:54Z
    4. 醇>

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

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

    1.  let变量仅在其最近的封闭块(let value = i;)中可见。
    2.  for个变量只能在声明变量之后出现的代码行中使用(即使它们被悬挂!)。
    3.  let变量不得由后续{ ... }let重新声明。
    4. 全局let变量未添加到全局var对象。
    5.  let变量易于使用带闭包(它们不会导致竞争条件)。
    6. 醇>

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

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

      如果在循环中使用闭包(#5)或在代码中声明外部可见的全局变量(#4)时确定需要单绑定效果,那么

      let可能仍然有用。如果 let ,可以取代使用let进行出口>从转换器空间迁移到核心语言。

      实施例

      1。在最近的封闭区域外没用: 这段代码将抛出一个引用错误,因为var的第二次使用发生在用var声明它的块之外:

       export

      相比之下,x的相同示例有效。

      2。申报前没用:
      在代码可以运行之前,这段代码将抛出let,因为在声明之前使用了

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

      相比之下,ReferenceError的相同示例在不抛出任何异常的情况下进行分析和运行。

      第3。没有重新声明: 以下代码演示了使用x声明的变量以后可能不会重新声明:

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

      4。全球未附加到var

       let

      5。易于使用的封口: 使用

      let x = 1;
      let x = 2;  // SyntaxError: Identifier 'x' has already been declared
      
      声明的变量不适用于循环内的闭包。这是一个简单的循环,它输出变量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
      

      具体来说,这会输出:

       var

      在JavaScript中,我们经常在比创建变量时更晚的时间使用变量。当我们通过使用传递给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
      
      ,输出就会保持不变。相反,如果我们使用setTimeout代替:  
      for (let i = 0; i < 5; i++) {
          setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
      }
      

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

       let     
    13
    2017-05-22 01:21:34Z
    1. #5不是由竞争条件引起的。通过使用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
      
      var在闭包之外,到let执行时,var i = 0; while (i < 5) { doSomethingLater(); i++; }已经增加了5次,因此输出为i五次。通过使用doSomethingLater(),变量i在闭包内,因此每个异步调用都获得自己的i is 5副本,而不是使用let创建的'global'。
      2017-06-02 01:12:01Z
    2. @ DanielT。:我不认为从循环初始化器中提取变量定义的转换解释了什么。这只是i语义的正常定义。更精确的转换,虽然更复杂,但是经典的i i是${j} var,它引入了一个“函数激活记录”来保存for的每个值,其中for (var i = 0; i < 5; i++) { (function(j) { setTimeout(_ => console.log(的名称在函数内。
      2017-07-25 07:13:26Z
    3. 醇>

    可能以下两个函数显示差异:

     ), 125/*ms*/); })(i); }     
    11
    2016-11-26 16:18:38Z
    1. 关于它的另一个有趣的观点: jsfiddle.net/6rk8wpso
      2016-02-08 01:29:51Z
    2. 醇>
    i很有意思,因为它允许我们做这样的事情:  j

    导致计数[0,7]。

    尽管

     
    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
    }
    

    仅计算[0,1]。

        
    11
    2016-11-26 16:34:26Z
    1. 这是我第一次看到有人像变量阴影这样做是可取的。不,let的目的不是启用阴影
      2016-11-24 00:37:34Z
    2. 目的?它是一个构造,你可以随意使用它,其中一个有趣的方法就是这样。
      2016-11-24 00:39:05Z
    3. 醇>

    功能VS块范围:

    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++);
                }
            }
        }
    })();
    
    之间的主要区别在于用
    (() => {
        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++);
                }
            }
        }
    })();
    
    声明的变量是功能范围。而用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 
    
    声明的变量foo仍然可以在var语句之外访问。此变量testVar可在var 功能的范围内无处不在

    变量if

    当调用第二个函数foo时,使用testVar声明的变量bar只能在let语句中访问。因为用testLet声明的变量是块范围(其中块是大括号之间的代码,例如let,if,let)。

     if{}个变量未被提升:

    for{}function{}之间的另一个区别是声明的变量let 不要悬挂。一个例子是说明这种行为的最佳方式:

    变量var 被悬挂:

     let

    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(如果确实需要,请务必仔细考虑)。     
    11
    2018-09-10 07:39:18Z

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

    这不会产生编译错误:

     var

    这将:

     var     
    7
    2016-11-28 09:31:05Z

    window是全球范围(可提升)变量。

    var x = 1;
    var x = 2;
    
    let x = 1;
    let x = 2;
    
    是块范围。
      

    test.js

     var
        
    6
    2017-10-28 12:42:16Z

    使用let

    const关键字将变量声明附加到它所包含的任何块(通常是

    {
        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并且ES5中的变量在函数中具有范围,这意味着变量在函数内有效,而不在函数本身之外。

    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

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

    关于范围的最着名的访谈问题之一也足以完全使用varwindow,如下所示;

    使用

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

        
    6
    2018-05-22 13:22:19Z

    如果我正确阅读规格,那么也可以利用var 谢天谢地来避免自调用函数用于模拟私有成员 - 一种流行的设计模式,降低了代码的可读性,使调试变得复杂,代码保护或其他好处 - 除了满足某人对语义的渴望,所以停止使用它。 /咆哮

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

    请参阅“模拟私人界面

        
    6
    2019-01-12 05:29:57Z

    var的一些黑客攻击:

    1

     let

    2

     
    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
    

    3

     let

    Getter和setter with
        let statistics = [16, 170, 10];
        let [age, height, grade] = statistics;
    
        console.log(height)
    

     
        let x = 120,
        y = 12;
        [x, y] = [y, x];
        console.log(`x: ${x} y: ${y}`);
    
        
    3
    2016-11-26 16:44:48Z
    1. 请问这是什么意思
          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"
      
      ?使用3个属性类型/名称/值创建一个新对象,并使用节点?
      中的属性值初始化它们
      2017-06-15 07:55:34Z
    2. 在示例3中,您重新声明导致异常的节点。这些例子也与let完美配合。
      2019-01-09 10:57:08Z
    3. 醇>

    让vs var。这完全是关于范围

    var变量是全局的,基本上可以在任何地方访问,而让变量不是全局的,只有在右括号杀死变量时才存在。

    请参阅下面的示例,并注意lion(let)变量在两个console.logs中的行为方式不同;它变得超出了第二个console.log。

    的范围  
    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)
    
    3
    2019-04-18 00:49:32Z

    让我们成为es6的一部分。这些功能将以简单的方式解释差异。

     let { type, name, value } = node;     
    2
    2017-12-17 10:47:31Z

    以前在JavaScript中只有两个范围,即功能范围和全局范围。使用'var'关键字,JavaScript现在引入了

    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.
    
    个变量。

    要全面了解'let'关键字, ES6:'let'关键字在JavaScript中声明变量 会有所帮助。

        
    1
    2016-11-26 16:27:58Z

    现在我认为使用

    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
    }
    
    对语句块有更好的变量范围:  let

    我认为人们会在这之后开始使用let,这样他们就会像其他语言,Java,C#等在JavaScript中使用类似的范围。

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

    使用block-level不支持吊装。

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

    请参阅 ES6 In Depth :让和const 更好地理解它。

        
    1
    2016-11-26 16:33:53Z
    1. 为了深入理解它,请参考链接 - davidwalsh.name/for-and-against-let
      2016-07-01 08:27:18Z
    2. 醇>

    本文明确定义了var,let和const

    之间的区别
      

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

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

    https://medium。 COM /JavaScript的场景/JavaScript的ES6-VAR-让​​-或const的-ba58b8dcde75#.esmkpbg9b

        
    1
    2017-04-30 17:11:41Z

    如上所述:

      

    区别在于范围界定。 const的范围限定为最近的函数   块let的范围是最近的封闭块,其中   可以小于功能块。如果不在外,两者都是全球的   block.让我们看一个例子:

    例1:强>

    在我的两个例子中,我有一个功能varvar包含一个变量let等于10。 在我的第一个例子中,我检查myfunc是否等于10(myfunc)。如果是,我使用myvar关键字声明变量myvar(现在我有两个myvar变量)并为其分配一个新值(20)。在下一行我在我的控制台上打印它的值。在条件块之后,我再次在我的控制台上打印myvar==10的值。如果查看myvar的输出,var的值等于20.

    例2:强> 在我的第二个例子中,我没有在条件块中使用myvar关键字,而是使用myfunc关键字声明myvar。现在,当我拨打var时,我得到两个不同的输出:myvarlet

    所以区别很简单,即范围。

        
    1
    2018-08-13 14:02:08Z
    1. 请不要发布代码图片,因为它不会被未来用户搜索(以及可访问性问题),所以它被认为是SO的不良做法。同样,这个答案也没有增加任何其他答案尚未解决的问题。
      2018-08-24 17:29:59Z
    2. 醇>

    看一下这张图片,我创建了一个非常简单的示例来演示myfuncmyvar=20变量。如您所见,当您尝试更改myvar=10变量时,您将收到错误(尝试覆盖'name',这是常量'),但请查看const变量...

    首先我们声明let,然后分配一些其他值const,这没关系,当我们尝试更改let变量时我们没有任何错误

        
    1
    2019-02-16 17:26:48Z

    我认为这些术语和大多数例子都有点压倒性的, 我亲身区别的主要问题是理解“块”是什么。 在某些时候我意识到,除了let age = 33语句之外,一个块将是任何大括号。 函数或循环的开括号age = 34;将定义一个新块,其中任何用let定义的东西,在相同的东西(函数或循环)的闭括号IF之后将不可用; 考虑到这一点,它更容易理解:

     {
        
    1
    2019-04-28 02:21:13Z

    由于我目前正在努力深入了解JavaScript,我将分享我的简短研究,其中包含已经讨论过的一些重要内容以及其他一些不同视角的细节。

    如果我们了解功能块范围之间的区别,那么理解 var 之间的区别会更容易>

    让我们考虑以下情况:

     let

    }被调用时,会创建一个 ExecutionContext ,其中包含 VariableEnvironment 和所有与每次迭代相对应的 LexicalEnvironments

    更简单的例子

    功能范围

     
    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);

    块范围

     
    (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]
    
        
    1
    2019-05-07 22:47:51Z

    我想将这些关键字链接到执行上下文,因为执行上下文在所有这些中都很重要。执行上下文有两个阶段:创建阶段和执行阶段。此外,每个执行上下文都有一个可变环境和外部环境(它的词汇环境)。

    在执行上下文的创建阶段,var,let和const仍将存储它内存中的变量,在给定的执行上下文的变量环境中具有未定义的值。不同之处在于执行阶段。如果使用引用为var定义的变量,则在赋值之前,它将是未定义的。不会有例外。

    但是,在声明之前,不能引用用let或const声明的变量。如果在声明之前尝试使用它,则会在执行上下文的执行阶段引发异常。现在变量仍将在内存中,由执行上下文的创建阶段提供,但引擎将不允许您使用它:

     timer()

    如果使用var定义变量,如果Engine无法在当前执行上下文的变量环境中找到变量,那么它将向上移动作用域链(外部环境)并检查外部环境的变量环境中的变量。如果在那里找不到它,它将继续搜索范围链。 let和const不是这种情况。

    let的第二个特性是它引入了块范围。块由花括号定义。示例包括功能块,if块,块等。当您在块内部声明变量时,该变量仅在块内部可用。实际上,每次运行块时,例如在for循环中,它都会在内存中创建一个新变量。

    ES6还引入了const关键字来声明变量。 const也是块作用域。 let和const之间的区别在于const变量需要使用初始化程序声明,否则会产生错误。

    最后,当涉及执行上下文时,用var定义的变量将附加到'this'对象。在全局执行上下文中,它将成为浏览器中的窗口对象。这不是let或const的情况。

        
    0
    2019-02-13 16:07:22Z
    function test() {
        for(var z = 0; z < 69; z++) {
            //todo
        }
        //z is visible outside the loop
    }
    
来源放置 这里