如何修复 jslint 错误“不要在循环中创建函数。”?

IT技术 javascript jslint
2021-02-02 10:34:13

我正在努力使我们所有的 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 实现的一部分,其中扩展现有类的类在调用扩展类的成员时保留扩展类的 super 属性。澄清一下,上述实现的灵感来自John Resig 的这篇博文

但是我们也有在循环中创建的其他函数实例。

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

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

6个回答

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);
}
函数生成器更好,因为它实际上可以正常工作。参见stackoverflow.com/questions/750486/...
2021-03-15 10:34:13
@AshClarkemake_handler没有为循环的每次迭代重新创建是正确的,但对于循环make_handler每次迭代,返回的值仍然是一个新的函数对象此答案中的代码所做的只是向代码检查器隐藏每次迭代都会创建一个新函数,但不会阻止每次迭代创建新函数。比较多次调用make_handlerwith的返回值===并亲自查看。
2021-03-21 10:34:13
@Louis 是的,是的;我应该更明确。我确实试图理解make_handler函数的创建没有被一遍又一遍地评估,但这是我应该对返回值提出的一个很好的观点。
2021-03-24 10:34:13
@Gili:创建 make_handler 函数的代码在它说的时候执行function make_handler(div_id)一旦进入for循环,make_handler现在是对该函数的引用,并且不会为循环的每次迭代重新创建。
2021-03-26 10:34:13
“函数生成器”如何比每次循环迭代创建一个新函数更好?不是一样的吗?
2021-03-28 10:34:13

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

如果您在循环中创建一个函数,则会为循环的每次迭代创建一个函数实例。除非每次迭代生成的函数实际上都不同,否则使用将函数生成器置于循环之外的方法——这样做不仅仅是 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,它们很多而且很钝。

虽然没有声明变量,但这是伪代码……当您调用 handler(div_id); 时也是如此。它返回一个函数,在此示例中单击之前它不会运行警报。这是一个完整的示例:jsfiddle.net/scottux/RWCje
2021-03-16 10:34:13
当您调用 handler(div_id) 时,您实际上运行了将显示警报的函数。这将在 for 运行时发生,而不是在用户单击 div 时发生。
2021-03-17 10:34:13
请为变量(处理程序,div_id)添加变量。
2021-03-26 10:34:13
+1但是当函数在每次循环迭代中实际使用不同的变量时,我认为这是一个误报。使用“函数生成器”并不比在循环内实例化它更好。
2021-04-07 10:34:13

从字面上看,通过执行以下操作解决问题:

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

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

我明白你的意思了。如果你从字面上理解这个问题,它实际上是正确的答案。否决票是因为我认为这是不好的建议。任何提出这个问题的人都需要接受教育。所以除非答案被编辑,否则我不会让我恢复我的反对票......
2021-03-31 10:34:13

JSLint 只是一个指南,您不必总是遵守规则。问题是,您不是在循环中创建函数,就像它所指的那样。您只能在应用程序中创建一次类,而不是一遍又一遍。

我知道 jslint 只是一个推荐,它旨在用于发现错误的 JS 代码,但它仍然是一个很好的工具,可用于在自动构建/测试环境中对代码进行资格预审和完整性检查。正如我所写,我对使代码通过 jslint 的解决方法感兴趣,而不是完全避免运行 jslint。
2021-03-27 10:34:13
当然,这些解决方案是显而易见的。重点仍然是,您仍在循环内创建函数,您只是以一种可以说更复杂的特定方式编写它,只是为了安抚某些工具。不管怎样,如果这是你想做的,那就去做吧。
2021-03-29 10:34:13
也许本讨论中的其他人已经涵盖了这一点,但我没有看到:我觉得建议的解决方案更好,暗示您在循环中绑定一个函数,而不是创建它。从技术上讲,你是(显然),但我的理解是 JSLint 给你这个提示,因为函数应该在运行时可用,在定义迭代器或循环限制之前。在这种情况下,如果迭代器导致循环的 0 次迭代,则该函数仍被声明但未绑定。至于 javascript 如何实际解释这一点,我假设那部分没有实际意义。
2021-04-05 10:34:13
是的,我仍然在循环中创建函数,我的问题是我希望这段代码通过 jslint。现在,Skilldrick 和 Matt Eberts 给了我已经测试过的可行解决方案,现在我的代码既有效又通过了 jslint。我是一个老 C/C++ 程序员,我习惯将语法检查作为编译阶段的一部分。Javascript 缺乏编译,jslint 与使用带有警告的现代 c/c++ 编译器一样接近。
2021-04-07 10:34:13
当然,但是如果您查看下面的答案,您仍然在循环中创建一个函数。您添加额外的代码只是为了安抚 JSLint。你为什么要这样做?
2021-04-10 10:34:13

如果您正在使用 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);
}