关于闭包和内部IIFE的一些问题

时间:2014-12-08 04:40:56

标签: javascript closures iife

我正在javascriptissexy.com.上阅读这个tutualial,直到最后一个例子。

function celebrityIDCreator (theCelebrities) {
    var i;
    var uniqueID = 100;
    for (i = 0; i < theCelebrities.length; i++) {
        theCelebrities[i]["id"] = function (j)  { // the j parametric variable is the i passed in on invocation of this IIFE
            return function () {
                return uniqueID + j; // each iteration of the for loop passes the current value of i into this IIFE and it saves the correct value to the array
            } (); // BY adding () at the end of this function, we are executing it immediately and returning just the value of uniqueID + j, instead of returning a function.
        } (i); // immediately invoke the function passing the i variable as a parameter
    }

    return theCelebrities;
}

var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];

var createIdForActionCelebs = celebrityIDCreator (actionCelebs);

var stalloneID = createIdForActionCelebs [0];
console.log(stalloneID.id); // 100

var cruiseID = createIdForActionCelebs [1];
console.log(cruiseID.id); // 101

首先,我没有看到IIFE,或者至少在我熟悉的语法中,其中有(function()...带有前导左侧括号。此外,

 return function () {
     return uniqueID + j; // each iteration of the for loop passes the current value of i into this IIFE and it saves the correct value to the array
  } (); 

为什么这两个returns?如果这意味着IIFE不需要周围的括号?另外,j如何不像前一个例子中i那样立即增加到数组的长度?关于访问theCelebrities[i]["id"] = function (j) {参数的元素id的行actionCelebs。为什么最初需要为每个元素将该属性设置为0

上一个例子

// This example is explained in detail below (just after this code box).​
function celebrityIDCreator (theCelebrities) {
    var i;
    var uniqueID = 100;
    for (i = 0; i < theCelebrities.length; i++) {
      theCelebrities[i]["id"] = function ()  {
        return uniqueID + i;
      }
    }

    return theCelebrities;
}

var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];

var createIdForActionCelebs = celebrityIDCreator (actionCelebs);

var stalloneID = createIdForActionCelebs [0];
console.log(stalloneID.id()); // 103    

此处所有值都设置为103

1 个答案:

答案 0 :(得分:2)

您的问题的答案:

  1. IIFE代表立即调用函数表达式。在JavaScript中,函数文字以两种形式之一出现:(1)完整函数定义语句,或(2)表达式。完整函数定义语句具有function作为语句的前导标记,这意味着该语句不是表达式,而只是函数定义。它有效地导致在当前作用域中创建一个本地(或全局范围的全局范围)变量,其名称等于函数定义语句中给出的函数名,并且值等于对函数的引用:

    function myFunction() {}
    myFunction();
    
  2. function出现在表达式中的任何地方(不是作为第一个标记)时,函数定义被称为&#34;表达式&#34;并且会自动创建任何局部变量,但可以通过分配它来存储在变量中,或者通过用一对括号跟随它来立即调用:

    var func = function() { return 7; };
    alert(func()); // 7
    var funcRes = function() { return 3; }();
    alert(funcRes); // 3
    

    严格地说,术语IIFE可以引用任何上下文,其中函数定义被表达化,然后通过用一对括号跟随它来立即调用。您可以在代码示例中看到它们使用返回uniqueID + j的函数执行此操作,并且它们也使用包含所述函数的函数执行此操作,并简单地将其返回值转发到其父作用域,因此它们都符合IIFE的条件

    根据我的经验,术语IIFE最常用于指代包含整个陈述的IIFE,因此前导左括号对于表达函数定义是必要的,例如:

    (function() {
        var localVar = 3;
        alert(localVar);
    })();
    

    我认为这就是为什么你发现代码示例略有意外的原因。但是,根据对IIFE的严格定义,代码示例中的这两个函数定义无疑都符合IIFE的要求;只要function关键字 not 作为语句中的第一个标记出现,它就会被表达化,并且可以立即调用以形成IIFE。

    1. 这两个return只是代码设计的一部分。不可否认,整个过程非常简单,并且有一种更简单的方法来完成任务,即循环遍历数组的简单循环,并将id散列键的值分配给从{增加的数字{1}},但我想他们正试图展示IIFE。内部返回表达式计算来自uniqueIDuniqueID的下一个id值,它们都被内部函数关闭,并从内部IIFE返回,然后从外部返回值返回值外部IIFE返回j函数范围,可以在其中分配。

    2. 不,IIFE不需要周围的括号。要表达函数,所需的只是celebrityIDCreator关键字作为语句的第一个标记出现。

    3. 完整的闭包过程如下:当一个函数被定义并包含一个变量,其标识符不绑定到任何局部变量(用function或函数参数声明的变量)时,它就会关闭围绕最近的祖先(思考解析树)本地具有相同的标识符,或全局如果它不能绑定到任何祖先的本地。重要的是:它是由闭包捕获的变量引用 变量值

    4. 在&#34;上一个例子&#34;位于var函数范围内的本地i周围的i个闭包。重要的是,函数立即被调用,但对函数的引用被存储为celebrityIDCreator散列键的实际值。稍后在打印期间调用它。这意味着当函数最终被调用时,变量id已经完成循环数组,并且现在保留其最终值103.如果函数已被立即调用,则i将实际上是在循环期间进行评估,它将解析为当前值,这将是正确的递增值。

      在主代码示例中,内部IIFE中的i围绕外部IIFE的函数参数进行闭包,这是临时的,永远不会递增;该参数j是与j函数范围中的i不同的变量,它永远不会关闭。此外,内部IIFE正在被立即调用,因此它无论如何都会解析为变量的当前值。所以他们实际上在这里过度了;最终指定值是正确的递增值而不是最大值(103)有两个原因。

      要点:围绕变量关闭闭包,而不是值。

      1. celebrityIDCreator属性最初不需要设置为0;这似乎只是一个占位符。在id中分配时,它会被覆盖(在您的主要示例和#34;上一个示例&#34;中)。因为在&#34;之前的例子&#34;它不是被另一个数字覆盖而是通过函数覆盖,这有点奇怪。同样,整个事情有点做作(对作者没有冒犯......),但所有的概念都存在。