题 如何修复jslint错误'不要在循环中创建函数'。


我正在努力使我们的所有JS代码都通过jslint传递,有时需要进行大量调整以获得遗留代码的选项,以便稍后正确修复它。

jslint有一件事抱怨我没有工作。那就是当使用这样的结构时,我们得到错误'不要在循环中创建函数'。

for (prop in newObject) {
    // Check if we're overwriting an existing function
    if (typeof newObject[prop] === "function" && typeof _super[prop] === "function" &&
        fnTest.test(newObject[prop])) {
        prototype[prop] = (function(name, func) {
            return function() {
                var result, old_super;

                old_super = this._super;
                this._super = _super[name];
                result = func.apply(this, arguments);
                this._super = old_super;

                return result;
            };
        })(prop, newObject[prop]);
    }
}

此循环是经典继承的JS实现的一部分,其中扩展现有类的类在调用扩展类的成员时保留扩展类的超级属性。 只是为了澄清,上面的实现是受此启发的 博客文章 作者:John Resig。

但是我们还在循环中创建了其他函数实例。

到目前为止唯一的解决方法是从jslint中排除这些JS文件,但我们希望使用jslint进行代码验证和语法检查,作为我们持续集成和构建工作流的一部分。

有没有更好的方法来实现这样的功能或有没有办法通过jslint调整这样的代码?


59
2018-06-14 13:33


起源




答案:


Douglas Crockford有一种新的惯用方法来实现上述目标 - 他的旧技术是使用内部函数来绑定变量,但新技术使用函数制作器。看到 幻灯片中的幻灯片74转到他的“功能终极”谈话。 [此幻灯片不再存在]

对于懒惰,这里是代码:

function make_handler(div_id) {
    return function () {
        alert(div_id);
    };
}
for (i ...) {
    div_id = divs[i].id;
    divs[i].onclick = make_handler(div_id);
}

65
2018-06-14 15:36



“函数制造者”如何比每次循环迭代创建一个新函数更好?这不是一回事吗? - Gili
@Gili:创建make_handler函数的代码在它说的那一点执行 function make_handler(div_id)。一旦进入 for 循环, make_handler 现在是对该函数的引用,并且不会为循环的每次迭代重新创建。 - Ash Clarke
@AshClarke这是正确的 make_handler 不会为循环的每次迭代重新创建,但它仍然适用于循环的每次迭代 返回的值 make_handler 是一个新的功能对象。这个答案中的代码是什么 隐藏 从代码检查器开始,每次迭代都会创建一个新函数但不会 避免 每次迭代创建一个新函数。比较多个调用的返回值 make_handler 同 === 并亲自看看。 - Louis
@Louis是的,是的;我本来应该更明确。我确实试图了解这个的创造 make_handler 函数没有被反复评估,但这是我应该对返回值做出的一个好点。 - Ash Clarke
功能制造商更好,因为它实际上将正常工作。看到 stackoverflow.com/questions/750486/... - Vadim


(我发布这个问题几个月后我偶然发现了这个问题......)

如果在循环中创建函数,则为循环的每次迭代创建函数的实例。除非正在进行的函数实际上对于每次迭代都是不同的,否则使用将函数生成器放在循环外部的方法 - 这样做不仅仅是Crockery,它让读取代码的其他人知道这是你的意图。

如果函数实际上是在迭代(或迭代中生成的对象)中分配给不同值的相同函数,那么您需要将函数分配给命名变量,并在函数中使用该函数的单个实例。循环:

handler = function (div_id) {
    return function() { alert(div_id); }
}

for (i ...) {
    div_id = divs[i].id;
    divs[i].onclick = handler(div_id);
}

当我在Stack Overflow上提出类似问题时,其他人比我更聪明,对此进行了更多的评论/讨论: JSlint错误'不要在循环中创建函数'。引出有关Javascript本身的问题

至于JSLint: 是的,这是教条和惯用语。也就是说,它通常是“正确的” - 我发现许多对JSLint负面发声的人实际上并不理解(微妙的)Javascript,它们很多而且很迟钝。


12
2018-01-21 12:14



请为变量添加变量(handler,div_id)。 - Ohad Kravchick
当您调用处理程序(div_id)时,您实际运行将显示警报的函数。这将在for运行时发生,而不是在用户单击div时发生。 - Ohad Kravchick
虽然没有声明vars,但这是伪代码...当你调用handler(div_id)时也是如此。它返回一个函数,在此示例中单击之前它不会运行警报。这是一个完整的例子: jsfiddle.net/scottux/RWCje - Scottux
+1但我认为当函数实际上每循环迭代使用不同的变量时这是假阳性。使用“函数制造者”并不比在循环中实例化它更好。 - Gili


从字面上看,得到 周围 通过执行以下操作来解决问题:

  1. 创建一个 .jshintrc 文件
  2. 将以下行添加到您的 .jshintrc 文件

    {"loopfunc" : true, // tolerate functions being defined in loops }


10
2017-10-15 15:11



这次投票是完全荒谬的! - lifebalance
我明白你的意思。如果从字面上理解这个问题,实际上是正确的答案。投票结果是因为我认为这是一个糟糕的建议。任何提出这个问题的人都需要接受教育。除非答案被编辑,所以不允许我恢复投票... - Thijs Koerselman
@ThijsKoerselman我刚刚编辑过它...... - lifebalance


JSLint只是一个指南,您并不总是必须遵守规则。问题是,你不是在它所指的意义上在循环中创建函数。您只在应用程序中创建一次类,而不是一次又一次。


7
2018-06-14 13:40



我知道jslint只是一个推荐文件,它的目的是用来发现糟糕的JS代码,但它仍然是一个很好的工具,用于在自动构建/测试环境中预先验证和理智检查代码。正如我写的那样,我对使代码传递jslint的解决方法感兴趣,而不是避免运行jslint。 - Ernelli
当然,但如果你看下面的答案,你仍然在循环中发挥作用。您只是为了安抚JSLint而添加额外的代码。你为什么要这样做? - Evan Trimboli
是的我仍然在循环中创建函数,我的问题是我希望这段代码传递jslint。现在,Skilldrick和Matt Eberts给了我工作解决方案,我已经测试了,现在我的代码都可以工作并传递jslint。我是一个旧的C / C ++程序员,我习惯于将语法检查作为编译阶段的一部分。 Javascript缺乏编译,jslint与使用带有警告的现代c / c ++编译器一样接近。 - Ernelli
当然,这些解决方案是显而易见的。关键在于,你仍然在循环中创建函数,你只是以特定的方式编写它,这可能更复杂,只是为了安抚一些工具。无论如何,如果你想做什么,那就去吧。 - Evan Trimboli
也许在本次讨论中其他人已经涵盖了这一点,但我没有看到它:我觉得提议的解决方案更好,暗示你在循环中绑定一个函数,而不是创建它。从技术上讲,你(显然),但我的理解是JSLint为你提供了这个提示,因为在定义迭代器或循环限制之前,函数应该在运行时可用。在这种情况下,如果迭代器导致循环的0次迭代,则仍然声明该函数但不绑定。至于javascript实际上如何解释这个,我假设那部分是没有实际意义的。 - NateDSaint


如果您使用的是JQuery,您可能希望在循环中执行以下操作:

for (var i = 0; i < 100; i++) {
  $("#button").click(function() {
    alert(i);
  });
}

为了满足JSLint,解决这个问题的一种方法是(在JQuery 1.4.3+中)使用额外的处理程序数据参数 .click()

function new_function(e) {
  var data = e.data; // from handler
  alert(data); // do whatever
}

for (var i = 0; i < 100; i++) {
  $("#button").click(i, new_function);
}

5
2018-02-20 00:36





移动你的:

(function (name, func) {...})()

阻止循环并将其分配给变量,如:

var makeFn = function(name, func){...};

然后在循环中有:

prototype[prop] = makeFn(...)


3
2018-06-14 15:31



你应该学会更好地格式化你的答案。我会为你编辑这个。 - TheCarver