【问题标题】:how to interpret the result ( understand let and var in js)如何解释结果(了解 js 中的 let 和 var)
【发布时间】:2019-10-05 06:40:48
【问题描述】:

考虑下面的代码

var fs = [];

for(var i=0;i<10;i++){
	fs.push(i => console.log(i));
}

fs.forEach( f => f());

如果函数改为:

for(let i=0;i<10;i++){
    fs.push(function(){
        console.log(i)
    });
}

它将打印 1,2,3,4,5,6,7,8,9 的预期输出。

我不明白为什么。有人可以帮忙吗?

【问题讨论】:

    标签: javascript arrays function loops ecmascript-6


    【解决方案1】:

    let 和 var 对您的代码没有影响关于为什么您获得 10 倍undefined

    有所不同的是,您的箭头函数是使用参数i(覆盖外部索引i)定义的,而在第二个示例中则不是。

    const fs = [];
    
    for (var i = 0; i < 10; i++) {
      fs.push(() => console.log(i));
    }
    
    fs.forEach(f => f());

    如果将该参数添加到第二个示例中,您还将获得 10x undefined

    const fs = [];
    
    for (let i = 0; i < 10; i++) {
      fs.push(function(i) {
        console.log(i)
      });
    }
    
    fs.forEach(f => f());

    您的代码中varlet 之间的区别更加微妙:

    const fs = [];
    
    for (var i = 0; i < 10; i++) {
      fs.push(() => console.log(i));
    }
    
    fs.forEach(f => f());

    const fs = [];
    
    for (let i = 0; i < 10; i++) {
      fs.push(() => console.log(i));
    }
    
    fs.forEach(f => f());

    对于let,数组中的每个函数都有一个局部范围的闭包i,而对于var,在执行时i10(因为只有一个变量i是闭包的在每个函数中)。

    【讨论】:

    • 如果我通过 do [ i => console.log(i) ] 它仍然会向我打印 'undefined' ,在这种情况下箭头函数是否会帮助我读取外部范围变量我
    • 不,这仅适用于this
    • 啊好的..明白了,非常感谢
    【解决方案2】:

    有 2 个不同的问题需要详细讨论。让我们关注第一种情况,主要问题是你定义了一个 ES6 箭头函数,然后在没有参数的情况下调用它:

    i =&gt; console.log(i) 转换为 ES5 匿名函数表示法时为:

    function(i){ console.log(i) }
    

    所以在i =&gt; console.log(i) 中,你所拥有的是一个匿名函数(也称为ES6 arrow function notation)的简写ES6 定义,它接受一个参数i

    最终结果是console.log(i) 试图打印一个i,它是undefined,因为它在执行时没有传递给那个箭头函数

    您正在推送一个函数定义,稍后您将执行 而不传递给它它需要在控制台中实际输出它的参数。

    var fs = [];
    
    for(var i=0; i<10; i++){
      // You are pushing to the array the below anonymous function definition
      fs.push(  
        // You are creating an anonymous function which accepts i and console.logs it
        i => console.log(i)
      );  
    }
    
    fs.forEach(
      // You are calling the "pushed" above function definition with NO parameter i
      f => f()
    );

    现在让我们探索第二个代码示例的工作原理和方式,以及var/let 如何在控制台输出中发挥重要作用:

    let fs = []
    
    // You define i in the for-loop block score
    for(var i=0; i<10; i++){
       fs.push(
         // You push an annonymous function definition wich has a reference of i in the same block scope
         // However i is already here 10 since the loop is already done and it takes the last value of i
         function(){ console.log(i) }
       );
    }
        
    fs.forEach(
       // You call f which has a reference inside to the i from the for loop
       f => f()
    );

    因此,在这种情况下,i 当您使用 var i 时,i 因为它不保留其词法块范围,最终会在调用 console.log 之前更新为 10

    让我们现在用let试试这个:

    let fs = []
    
    // You define i via LET in the for-loop block score
    for(let i=0; i<10; i++){
      fs.push(
        // You push an annonymous function definition wich has a reference of i in the same block scope
        // i retains its lexical scope in for loops
        function(){ console.log(i) }
       );
    }
    
    fs.forEach(
      // You call f which has a reference inside to the i from the for loop
      f => f()
    );

    关于 let 的更多内容:

    let 允许您声明范围限于的变量 blockstatementexpression 使用它。这是不同 var 关键字,将全局变量或局部变量定义为 整个函数与块范围无关。

    【讨论】:

      【解决方案3】:

      在您的第一个示例中,fs.push(i =&gt; console.log(i)); 中的i 是箭头函数的参数。这实际上等同于fs.push(function(i){ return console.log(i); });

      此参数与循环的迭代器同名。在您推送的函数范围内,参数i 优先于外部变量。如果要捕获迭代器,则不应将其命名为与参数相同的名称,如下所示:

      fs.push(() =&gt; console.log(i));

      现在您将看到在这种情况下varlet 之间存在差异。您的输出将是 10,10,10,10,10,10,10,10,10,10。这是因为varlet 之间的范围不同。当使用var 声明时,循环迭代器将在整个循环期间是同一个对象。所有功能都在捕获同一个对象。循环结束后的最终值(i++ 发生,然后条件检查发生)是 10,因此它们都打印 10。使用let,迭代器是循环的每次迭代的新对象,因此每个函数捕获一个保存当前循环计数的新对象。

      【讨论】:

        猜你喜欢
        • 2016-07-01
        • 2016-03-08
        • 1970-01-01
        • 1970-01-01
        • 2019-02-12
        • 1970-01-01
        • 2018-06-14
        • 2021-08-12
        • 2021-05-17
        相关资源
        最近更新 更多