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

Javascript Closures and self-executing anonymous functions

本文关键字:函数 执行 闭包 Javascript      更新时间:2023-09-26

我在面试中被问到以下问题,我仍然不明白,所以我想寻求您的建议。

问题来了:

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。所以我通读了这篇文章,意识到我应该使用闭包来完成这个工作,比如:

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](),但它不起作用。产出现在被困在第3个计数上。

现在我真的不明白了。我只是使用与setInner()相同的代码行。所以我不明白为什么这行不通。事实上,我本可以坚持使用setInner这种代码结构,它确实有效,而且更全面。但是我真的很想知道面试官是怎么做的,以便更好地理解这个话题。

这里要阅读的相关文章是JavaScript闭包内循环-简单的实际示例和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",您实际上可以调用它们。

看一下这四个函数:

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中调用函数时,它们会获得在闭包中创建的局部变量的值。

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