如何解释结果(了解js中的let和var)

时间:2019-05-17 23:35:42

标签: javascript arrays function loops ecmascript-6

考虑以下代码

```{r, echo = FALSE, results='asis'}
t <-readLines("your_html.html")
len <- length(t)-1
cat(t[2:len])
```

如果功能更改为:

var fs = [];

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

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

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

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

3 个答案:

答案 0 :(得分:4)

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

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

const fs = [];

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

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

如果将该参数添加到第二个示例中,则您还将获得10倍的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

答案 1 :(得分:2)

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

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

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

因此,在i => console.log(i)中,您可以看到一个匿名函数(也称为ES6 arrow function notation)的简短ES6定义,该函数接受参数i

最终结果是console.log(i)试图打印i的{​​{1}},因为它没有在执行时传递给 arrow函数

您重新推送了一个函数定义,稍后您将执行而无需传递给它一个参数,该参数实际上需要在控制台中输出。

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,因为它不保留其词法块作用域,最终会在console.log之前更新为i被呼叫。

现在用10尝试一下:

let

让let多一点:

  

let允许您声明范围内限于的变量   阻止声明表达式。这是与众不同    var关键字,它定义了全局或本地变量   整个功能,无论块范围如何。

答案 2 :(得分:1)

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

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

fs.push(() => console.log(i));

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