【问题标题】:JavaScript Function inside the loop循环内的 JavaScript 函数
【发布时间】:2014-11-27 01:09:33
【问题描述】:

谁能用这个例子向我解释为什么 JSLint 抱怨“循环内的函数”:

  for (var i = 0; i < buttons.length; i++) {
    (function(i) {
      buttons[i].onclick = function(e) {
        t.progressBars[t.current].update(buttons[i].getAttribute("data-value"));
      }
    })(i);
  }

但是当我把它改成:

function makeHandler(i)
  {
    return function() {
        t.progressBars[t.current].update(buttons[i].getAttribute("data-value"));
      }
  }

  for (var i = 0; i < buttons.length; i++) {

      buttons[i].onclick = makeHandler(i);

  }

我不太明白,因为似乎每次循环迭代都必须返回新的函数对象,即使它发生在 makeHandler() 函数内部。为什么第二个例子可以用 JS linter?

【问题讨论】:

  • 第二个示例将 i 与回调绑定。第一个没有。在第一个示例中,您将拥有所有 i 值等于 n。其中第二个将有 0 到 n-1。
  • @mithunsatheesh - 再看一遍。第一个是 IFFE 并且执行相同的绑定。
  • 这只是 jsLint 的一个信息不足的警告。人们对它的警告给予了比它应得的更多的信任。它可以警告你不应该做的事情,但它警告的一切都不是问题,甚至不一定是应该改变的事情。
  • @TedHopp:天哪。我错过了什么吗? see this fiddle。不绑定。
  • @mithunsatheesh - 你是在提醒号码1,而不是i。这没有显示任何内容。

标签: javascript function jslint


【解决方案1】:

引用linterrors

var elems = document.getElementsByClassName("myClass"), i;
for (i = 0; i < elems.length; i++) {
    (function (iCopy) {
        "use strict";
         elems[i].addEventListener("click", function () {
            this.innerHTML = iCopy;
         });
    }(i));
}

我们现在在循环的每次迭代中捕获i 的值。发生这种情况是因为 JavaScript 按值将参数传递给函数。这意味着捕获函数中的iCopy 与 i 没有任何关系(除了它们在那个时间点恰好具有相同的值)。如果i 稍后发生更改(它会在循环的下一次迭代中更改),则iCopy 不受影响。

这将像我们预期的那样工作,但现在的问题是 JavaScript 解释器将在每次循环迭代时创建一个捕获函数的实例。它必须这样做,因为它不知道函数对象是否会在其他地方被修改。由于函数是标准的 JavaScript 对象,它们可以像任何其他对象一样具有可以在循环中更改的属性。因此,通过在循环上下文中创建函数,您会导致解释器创建多个函数实例,这可能会导致意外行为和性能问题。为了解决这个问题,我们需要将函数移出循环:

我本来想在这里使用Array.prototype.forEach,像这样

buttons.forEach(function(curButton) {
    curButton.onclick = function(e) {
        t.progressBars[t.current].update(curButton.getAttribute("data-value"));
    };
});

【讨论】:

  • 使用 IIFE 创建闭包是循环中的标准问题解决机制。我完全看不出有什么问题。如果您不需要闭包,则可以将代码移至外部定义的函数,因此它是函数调用而不是 IIFE,但这有一些权衡(无法访问父范围变量),声明了额外的符号,代码不是内联的为了可读性等...
  • @jfriend00 同意。我只是指出 jsLint 对这样的代码的看法 :-)
【解决方案2】:

你的两个例子不等价。

首先,您将创建一个匿名函数并在每个循环中调用它。

内部函数(点击事件处理程序)很好 - 您正在分配一个新函数 - 但在这种情况下,匿名外部函数效率低下。在您的第二个示例中,外部函数被重构出循环,它只创建一次,而不是 buttons.length 次。

【讨论】:

    猜你喜欢
    • 2021-10-02
    • 1970-01-01
    • 1970-01-01
    • 2015-07-14
    • 2018-07-03
    • 1970-01-01
    • 2013-09-09
    • 1970-01-01
    相关资源
    最近更新 更多