在接受采访时我被问到以下问题,我仍然无法理解,所以我想征求你的意见。
以下是问题:
var countFunctions = [];
for(var i = 0; i < 3; i++){
countFunctions[i] = function() {
document.getElementById('someId').innerHTML = 'count' + i;
};
}
//The below are executed in turns:
countFunctions[0]();
countFunctions[1]();
countFunctions[2]();
当被问及上面的输出是什么时,我分别说了count0,count1和count2。显然答案是错误的,并且输出应该都是count3,因为闭包的概念(我当时并不知道)。所以我经历了this article并意识到我应该使用闭包来完成这项工作,例如:
var countFunctions = [];
function setInner(i) {
return function(){
document.getElementById('someId').innerHTML = 'count' + i;
};
}
for(var i = 0; i < 3; i++){
countFunctions[i] = setInner(i);
}
//Now the output is what was intended:
countFunctions[0]();//count0
countFunctions[1]();//count1
countFunctions[2]();//count2
现在这一切都很好,但我记得面试官使用更简单的东西,使用这样的自动执行功能:
var countFunctions = [];
for(var i = 0; i < 3; i++) {
countFunctions[i] = (function(){
document.getElementById('someId').innerHTML = 'count' + i;
})(i);
}
我理解上面的代码的方式,我们正在跳过单独函数的声明,只是在for循环中调用并执行函数。
但是当我跑下面的时候:
countFunctions[0];
countFunctions[1];
countFunctions[2];
它不起作用,所有输出都停留在count2。
所以我试着改为:
for(var i = 0; i < 3; i++) {
countFunctions[i] = function(){
document.getElementById('someId').innerHTML = 'count' + i;
};
}
,然后运行countFunctions[0]()
,countFunctions[1]()
和countFunctions[2]()
,但它无效。输出现在停留在count3。
现在我真的不明白。我只是使用与setInner()
相同的代码行。所以我不明白为什么这不起作用。事实上,我可以坚持使用setInner
类型的代码结构,它可以工作,并且更全面。但是我真的很想知道面试官是如何做到这一点的,以便更好地理解这个话题。
答案 0 :(得分:1)
这里要阅读的相关文章是JavaScript closure inside loops – simple practical example和http://benalman.com/news/2010/11/immediately-invoked-function-expression/(尽管您似乎已经很好地理解了IEFE - 正如您所说,他们“跳过单独函数的声明并简单地调用并执行函数“)。
您没有注意到的是setInner
在调用时, return
关闭函数:
function setInner(i) {
return function() {
document.getElementById('someId').innerHTML = 'count' + i;
};
}
// then do
var countFunction = setInner("N"); // get the function
countFunction(); // call it to assign the innerHTML
因此,如果您将其翻译成IEFE,您仍然需要创建(并返回)实际分配给countFunctions[i]
的函数:
var countFunctions = [];
for(var i = 0; i < 3; i++) {
countFunctions[i] = (function(i){
return function() {
document.getElementById('someId').innerHTML = 'count' + i;
};
})(i);
}
现在,typeof countFunctions[0]
将是"function"
,而不是代码中的"undefined"
,您实际上可以调用。
答案 1 :(得分:0)
看看这四个功能:
var argument = 'G'; //global
function passArgument(argument){
alert(argument); //local
}
function noArguments(){
alert(argument); //global
}
function createClosure_1(argument){
return function (){
alert(argument); //local
};
}
function createClosure_2(argument){
var argument = argument; //local
return function (){
alert(argument); //local
};
}
passArgument('L'); //L
noArguments(); //G
createClosure_1('L') //L
createClosure_2('L') //L
alert(argument) //G
第三和第四个函数做同样的事情。它们创建一个本地参数变量,它不会在它们内部发生变化,并返回一个引用该局部变量的函数。
那么,你问题的第一个和最后一个代码片段中的内容是创建了很多函数,比如noArguments, 引用全局变量i。
在第二个片段中,您的setInner的工作方式与createClosure_1类似。在循环中,您将创建三个闭包,其中包含三个局部变量。当你在countFunctions中调用函数时,它们会获得在创建闭包时在闭包内创建的局部变量的值。
在第三个中,您将执行这些函数的结果分配给数组元素,这是未定义的,因为它们不会从该函数返回任何内容。