【问题标题】:let vs var in javascript [duplicate]javascript中的let vs var [重复]
【发布时间】:2018-06-08 17:02:06
【问题描述】:

我知道 let 具有块范围,而 var 具有功能范围。但是我不明白在这种情况下,使用 let 将如何解决问题

const arr = [1,2,3,4];
for (var i = 0; i < arr.length; i++) {
setTimeout(function() {
   console.log(arr[i]) 
}, 1000);
} // Prints undefined 5 times

const arr = [1,2,3,4];
for (let i = 0; i < arr.length; i++) {
setTimeout(function() {
   console.log(arr[i]) 
}, 1000);
} // Prints all the values correctly

【问题讨论】:

    标签: javascript closures var let


    【解决方案1】:

    这都与变量的作用域有关。让我们尝试将这两个部分包装成函数,并观察输出:

    function test() {
      // `i` will be declared here, making it a non-for-loop scoped variable
      const arr = [1, 2, 3, 4];
      for (var i = 0; i < arr.length; i++) {
        setTimeout(function() {
          console.log(arr[i])
        }, 1000);
      } // Prints undefined 5 times
    }
    
    test();

    所以在第一种情况下,i 将被提升,并且由于setTimeout 的异步特性,i 将立即变为 4,因为循环结束而无需等待。这将使arr[i] 指向数组中的undefined 元素。

    在第二种情况下,i 未被提升,并且对循环的每次迭代具有范围访问权限,从而使i 准确地可用于console.log 语句。因此结果符合预期:

    function test() {
      const arr = [1, 2, 3, 4];
      for (let i = 0; i < arr.length; i++) {
        setTimeout(function() {
          console.log(arr[i])
        }, 1000);
      } // Prints all the values correctly
    
    }
    
    test();

    【讨论】:

    • 我还是不明白。 let 是在 for 周围还是在 for 内部?有四种不同的i,还是一种?如果是一个,它与var 的情况有什么不同(大概i 仍然会被捕获,就像var 一样)?如果是四个,为什么i++ 有效(即旧的i 和新的i 是如何被访问的)?如果有人有 ES2018 的相关段落,那就太棒了。 (编辑:建议的副本有答案。)
    • 嗯,对不起,我没听懂。你能解释一下hoisted是什么意思吗?
    • In the second case, i is not hoisted, and has scoped access to each iteration of the loop, making i accurately available to console.log statement. 没错,但循环的执行不会因setTimeout 而停止。在这种情况下,循环也会结束,如果您在 settimeout 之外记录i 的值,您在settimeout 开始执行之前,将看到它的值i 设置为length-1。所以问题是settimeout 如何获得i 的值,即使它的值设置为length-1
    • 在第二个示例中,i 的绑定每次迭代都会发生变化,给人的印象是拥有单独的 i 副本。请仔细阅读原始帖子(此问题被标记为重复)以澄清更多信息。 :)
    • @Amadan 在 ES6+ 中 i 在块内,每次迭代都会创建一个新的词法环境。在“IterationStatement 下:for ( LexicalDeclaration Expression ; Expression ) Statement”在 ECMA2017 版本的第 13.7.4.7 节中,在第 2 步中创建循环环境并在第 7 步中设置到位。根据第 13.7.4.8 节,循环环境在第一次循环迭代(步骤 2)之前被复制,并在循环结束时以现有状态再次复制,在 `++i' 增量之前(步骤 3.e) 之前的环境在循环终止。
    【解决方案2】:

    首先,输出将是四次而不是五次(如您的评论中所述)。 我将您的代码粘贴到 Babel REPL 中,这就是我得到的,

    "use strict";
    
    var arr = [1, 2, 3, 4];
    
    var _loop = function _loop(i) {
    setTimeout(function () {
       console.log(arr[i]);
    }, 1000);
    };
    
    for (var i = 0; i < arr.length; i++) {
    _loop(i);
    }
    

    你现在看到 let 在内部是如何工作的了吗? :-)

    【讨论】:

      【解决方案3】:

      您仍然可以将var 用于setTimeout。您可以使用立即调用的函数表达式 (IIFE) 在 setTimeout 周围创建一个闭包,以便 i 的值被 setTimeout 函数识别。

      const arr = [1,2,3,4];
      for (var i = 0; i < arr.length; i++) {
      (function(i){
      setTimeout(function() {
         console.log(arr[i]) 
      }, 1000)})(i);
      }

      【讨论】:

      • 这并不能回答“为什么let 有效”的问题。
      • 感谢您的回答 Ankit。但我知道如何使用 IIFE 使其工作。我只是想知道“让”如何使这项工作发挥作用。
      猜你喜欢
      • 2016-04-05
      • 2014-11-16
      • 2020-09-29
      • 2017-08-18
      • 1970-01-01
      • 2016-09-04
      • 2019-12-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多