【问题标题】:jslint - Don't make functions within a loopjslint - 不要在循环中创建函数
【发布时间】:2013-03-17 21:21:00
【问题描述】:

参考这个话题:Don't make functions within a loop. - jslint error

您将如何处理 for 循环中的 jquery .each(function () {...}?知道我需要“每个”函数中的“for”上下文。当然,我可以将每个所需的映射到函数的参数是在循环之外声明的函数,但从我的角度来看,它会影响可读性。

有什么想法吗?

提前致谢。

【问题讨论】:

    标签: javascript jslint


    【解决方案1】:

    好吧,您可以在循环中保留 for 的上下文,因为 for 中的所有内容实际上与开始时声明的函数处于相同的上下文中。

    让我们以 Frits 的例子为例,但首先让我们先让 JSLint 完全满意(减去循环错误中调用的函数)。

    /*global console*/
    var my_func, i, list;
    for (i = 0; i < list.length; i+= 1) {
        my_func = function (i) {
            console.log(i);
        };
        my_func(i);
    }
    

    请注意,每次你迭代循环,你重新声明my_func函数。这不酷!为什么要一遍又一遍地重新声明相同的函数?

    提前声明,像这样:

    /*global console*/
    var my_func, i, list;
    
    my_func = function (i) {
        console.log(i);
    };
    
    for (i = 0; i < list.length; i+= 1) {
        my_func(i);
    }
    

    成功。现在您不必为每次迭代创建一个函数。而且,由于 JSLint 通过将所有 var 声明推到顶部来帮助您实现,您仍然可以获得相同的上下文。


    编辑: 作为@Flame points out,您不必使用jQuery each 提前声明函数,并且可以使用匿名函数,但提前声明也不错,尤其是如果您要在多个 each 调用中重用逻辑。主要的收获是理解 1.) 早期声明实践仍然有优势,以及 2.) jQuery 仍然会向您的函数发送参数(这里,我们称之为 index),尽管 JSLint 会(也不应该)抱怨eachs (jQuery sauce here) 中使用的匿名函数。

    如果您习惯于匿名 $.each(function() {}); 构造,最初会有点不直观,但它同样简单。

    /*global alert, $ */
    $( "li" ).each(function( index ) {
        alert( index + ": " + $(this).text() );
    });
    

    ...变成...

    /*global $, alert */
    var fnOutside = function(index) {
        alert( index + ": " + $(this).text() );
    };
    $( "li" ).each(fnOutside);
    

    这可能会造成短暂的混淆,因为函数调用没有参数,但 jQuery 将“index”参数推送到函数。如果你想省略命名,你可以抓住上下文 arguments 数组,看看它仍然被推到那里。

    Fiddle-ige

    也就是说,for 构造不会创建新的闭包。这可能让你担心。没问题!

    【讨论】:

    • 你是对的,这是让 jslint 满意并尽可能保持代码可读性的方法。这就是我在最初的问题中描述为影响可读性的解决方案。使用单个参数就可以了,但是如果您考虑使用 10...
    • 当然,很抱歉你的问题太简单了。对我来说,真正的症结是上面的粗体部分,特别是 在循环内声明会降低性能(因为您每次都重新声明相同的函数)并破坏逻辑“规范化”,这是足够的理由(imo ) 使其处于更难以阅读的状态,就在for 的上方。您总是可以使用复合对象来保持它的可读性(options = {var1: "val1", var2: "val2"...})或绑定到有用的东西。无论哪种方式,在这些情况下,我都会接受 JSLint 的建议。
    • @rufin 这可能会让人暂时感到困惑,因为函数调用没有参数,但是 jQuery 将“index”参数推送给函数。 jQuery 还推送了第二个参数,即元素,这可能比使用 $(this). /*global $, alert */ var fnOutside = function(index, el) { alert( index + ": " + $(el )。文本() ); }; $( "li" ).each(fnOutside);
    • 你做$('li').each(fnOutside)的最后一个例子没有任何区别。您只调用函数each 一次。一旦您进入该函数,您传递的参数fnOutside 将不会再次更改。此外,jslint/jshint 只会在您显式使用 for 循环时调用此类错误。它无法知道jQuery.each的内容
    • @Flame -- 你是对的。我不确定为什么两年前我觉得这很重要,除了明确表示 jQuery 仍会将参数推送到先前声明的函数中。我确实记得有些人在工作中很难做到这一点,也许这让我当时的回答蒙上了阴影。但是,再一次,你是对的,如果你在那里使用匿名函数,JSLint 不会(也不应该!)抱怨,因为它只声明了一次。 jQuery source here:each: function( obj, callback ) {...
    【解决方案2】:

    函数在循环中的问题是:this,

    for (var i = 0; i < list.length; i+= 1) {
        function my_func(i) {
            console.log(i);
        }
        my_func(i);
    }
    

    将被解释为:

    var my_func;
    for (var i = 0; i < list.length; i+= 1) {
        my_func = function (i) {
            console.log(i);
        }
        my_func(i);
    }
    

    因为可变提升。所有声明都将移到范围的顶部(即函数)。所以在 for 循环中定义函数实际上没有意义。

    如果你想在 for 循环中绑定到计数器,只需做一个闭包。

    要明确一点:

    $("..").each(function () {
        for(..) {
        }
    });
    

    没问题。

    for(..) {
        $("..").each(function () {
    
        });
    }
    

    不好,因为它相当于这样:

    var my_func;
    for(..) {
        my_func = function () {
            // ..
        }
        $("..").each(my_func);
    }
    

    【讨论】:

      【解决方案3】:

      听起来问题更多的是警告而不是您的代码。是的,在没有警告的情况下通过 JSLint 测试会很好,但其中一些可能不合理并导致不必要的代码重构。

      本问答中的讨论是相关的:Should I use JSLint or JSHint JavaScript validation? 其他 lint 工具比 JSLint 更灵活,因此您可以继续根据自己的编码偏好(不一定是 Crockford 的个人编码偏好)保持高效。

      我建议您考虑使用其他工具来检查您的 JavaScript,并且不要牺牲代码的可读性或可维护性。

      【讨论】:

      • 当然,但仍然......它迫使提出好的问题。 ruffin 关于函数被一遍又一遍地重新声明的评论显然是这条规则最终有意义的原因。