Javascript闭包和自动执行的匿名函数

时间:2014-05-18 04:25:44

标签: javascript closures

在接受采访时我被问到以下问题,我仍然无法理解,所以我想征求你的意见。

以下是问题:

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类型的代码结构,它可以工作,并且更全面。但是我真的很想知道面试官是如何做到这一点的,以便更好地理解这个话题。

2 个答案:

答案 0 :(得分:1)

这里要阅读的相关文章是JavaScript closure inside loops – simple practical examplehttp://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
  1. 我认为,第一个功能是显而易见的。
  2. 在函数noArguments中,您引用全局参数值;
  3. 第三和第四个函数做同样的事情。它们创建一个本地参数变量,它不会在它们内部发生变化,并返回一个引用该局部变量的函数。

    那么,你问题的第一个和最后一个代码片段中的内容是创建了很多函数,比如noArguments, 引用全局变量i。

    在第二个片段中,您的setInner的工作方式与createClosure_1类似。在循环中,您将创建三个闭包,其中包含三个局部变量。当你在countFunctions中调用函数时,它们会获得在创建闭包时在闭包内创建的局部变量的值。

    在第三个中,您将执行这些函数的结果分配给数组元素,这是未定义的,因为它们不会从该函数返回任何内容。