考虑以下代码
```{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预期输出。
我不明白为什么。有人可以帮忙吗。
答案 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());
您的代码中var
和let
之间的区别更加微妙:
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
中的i
是10
(因为存在每个函数中仅包含一个封闭的变量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));
现在,您会发现在这种情况下var
和let
之间存在差异。您的输出将是10,10,10,10,10,10,10,10,10,10,10。这是因为var
和let
之间的范围不同。当用var
声明时,循环迭代器将在循环持续时间内是同一对象。所有功能都捕获相同的对象。循环结束后(发生i++
,然后进行条件检查)的最终值为10,因此它们全部显示为10。使用let
,迭代器是循环每次迭代时的新对象,因此每个函数都捕获一个新对象,该对象保存了循环的当前计数。