【问题标题】:Scope with a self-invoking function in Javascript范围与 JavaScript 中的自调用函数
【发布时间】:2010-12-17 00:09:08
【问题描述】:

以下代码遍历 6 个输入按钮,并为每个按钮附加一个onclick 事件,以提醒相应迭代的索引号:

for (var i = 1; i < 6; ++i) {
    var but = document.getElementById('b_' + i);
    (function (el) {
        var num = i;
        but.onclick = function () {
            alert(num);
        };
    })(but);
}

如您所见,在每次迭代中都有一个自调用函数,该函数创建一个作用域来存储该作用域中的迭代索引。

我一直使用这种类型的模式来附加一个依赖于在迭代期间更改的变量的事件。


任何人都可以向我解释为什么上述工作,以及num 变量是如何在范围内捕获的?

另外,上面使用的自调用函数是否称为closure

【问题讨论】:

  • 是的,这肯定是一个闭包。
  • 那里有一个闭包,当然。请注意,stackoverflow.com/questions/111102/… 的一些答案也回答了您的问题,尽管另一个问题不同。
  • 就我而言,我不会称该匿名函数为“自调用”,这意味着“递归”。它是“立即调用”之类的。
  • 可能匿名函数会调用el.onclickiso but.conclick?
  • @Skilldrick 防止语法错误

标签: javascript scope closures


【解决方案1】:

是的,这是一个闭包。

每次执行一个函数时,都会创建一个新对象来保存(作为它的属性)使用var 声明的变量以及在其中声明的每个函数。该对象称为执行上下文(或有时称为范围对象)。

每次声明(或在表达式中定义)函数时,新函数都会将当前的执行上下文对象附加到它。这会创建所谓的作用域链。

当执行代码需要将标识符解析为一个值时,它首先在当前执行上下文的属性中查找它。如果未找到标识符,则使用附加到正在执行的函数的执行上下文对象。它一直在作用域链上向上,直到达到全局级别。

在您的示例中,每次执行“自调用函数”时都会创建一个新的执行上下文对象,其中包含属性elnum。由于分配给 onclick 的函数是在此执行上下文中创建的,因此您每次都会获得此函数的一个新实例。这些实例都将附加相应的执行上下文对象。因此,当num 被分配1 时,第一个将具有执行上下文,第二个将具有num 已被分配2 的执行上下文,依此类推。

当每个 onclick 函数运行时,代码最初会在当前执行上下文中查找标识符 num。然而,这个内部函数没有 var a num 所以它没有找到。因此,Javascript 在创建函数时会查看附加到函数的执行上下文。在这里它将找到numnum 将包含在迭代期间分配给它的值,如上所述。

【讨论】:

    【解决方案2】:

    大家好。是的,这是一个关闭。如果您想知道函数创建时究竟发生了什么,请研究以下文章。 http://www.jibbering.com/faq/faq_notes/closures.html

    【讨论】:

      【解决方案3】:

      这是否意味着在closure 之外定义的var i 是一个全局变量,从而使自调用函数可以访问它?

      【讨论】:

      • i 仅当在全局上下文中评估外部循环时才是全局的。这是因为 i 在定义函数时在范围内,这使得 i 在函数中可见。另外,如果您对某个问题有疑问,请发表评论。回答是为了回答,你现在有足够的积分离开cmets。
      • 但是每次循环迭代后自调用函数的作用域如何保留?
      • @outis,很抱歉,我显然还不习惯这种结构,不过我会记住你所说的。
      【解决方案4】:

      每次通过循环,评估function (el) {...} 都会创建一个新的匿名函数,该函数会立即被调用。在匿名函数中,创建了一个名为num的变量(因为JS不做静态变量,每次都是一个新的)。 num 在函数被调用时(即立即)被赋值为i。这为我们提供了 6 个匿名函数和 6 个nums,每个函数的值都为 1 到 6。

      每次调用匿名函数时,都会创建一个内部匿名函数并将其存储为点击处理程序。内部函数引用num。结果,创建了一个闭包,以确保当外部函数退出时,num 不会被破坏。如果内部函数被丢弃,num 很快就会跟进。

      【讨论】:

        【解决方案5】:
        for (var i = 1; i < 6; ++i) {
            var but = document.getElementById('b_' + i);
            (function (el) {
                var num = i;
                but.onclick = function () {
                    alert(num);
                };
            })(but);
        }
        

        现在让我们开始执行循环
        最初 i=1,but= id ='b1'的domelement
        现在是函数调用,
        好的,它调用内部函数(带参数el),参数值为but('b1') 并开始执行它,这就是调用实际上意味着现在执行它。
        现在在里面
        num 的新实例被分配 1
        但是.onclick 被分配了一个函数,因此存储在内存中的函数也看到它访问了 num,因此 num 现在是封闭变量,这意味着它的生命周期增加了,以便在调用时被 onclick 函数访问。

        下一次迭代
        现在 i=2,but= id ='b2' 的 domelement 的值 现在是函数调用,
        它使用参数值为 but(value='b2') 调用内部函数(带参数 el)。
        现在在里面
        num 的新实例被分配 2
        但是.onclick 被分配了一个函数,因此存储函数在内存中也看到它访问 num 所以 num 现在是封闭变量,这意味着它的生命周期增加了,以便在调用时被 onclick 函数访问。
        类似地,所有其他的都被执行,直到循环终止。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-10-12
          • 2011-11-15
          • 1970-01-01
          相关资源
          最近更新 更多