JSlint错误';Don';t在循环中生成函数;这就引出了关于Javascript本身的问题

JSlint错误';Don';t在循环中生成函数;这就引出了关于Javascript本身的问题,javascript,jslint,Javascript,Jslint,我有一些在循环中调用匿名函数的代码,类似于下面的伪示例: for (i = 0; i < numCards; i = i + 1) { card = $('<div>').bind('isPopulated', function (ev) { var card = $(ev.currentTarget); .... (i=0;i

我有一些在循环中调用匿名函数的代码,类似于下面的伪示例:

for (i = 0; i < numCards; i = i + 1) {
    card = $('<div>').bind('isPopulated', function (ev) {
        var card = $(ev.currentTarget);
        ....
(i=0;i{ 卡=$('').bind('isPopulated',函数(ev){ var卡=$(ev.currentTarget); .... JSLint报告错误“不要在循环中生成函数”。我喜欢保持代码JSLint干净。我知道我可以将匿名函数移出循环,并将其作为命名函数调用。除此之外,我的问题是:


Javascript解释器真的会在每次迭代中创建一个函数实例吗?还是真的只有一个函数实例“已编译”,并且重复执行相同的代码?也就是说,JSLint“建议”将函数移出循环实际上会影响代码的效率?

解释器可能会在每次迭代中实际创建一个新的函数对象,这仅仅是因为该函数可能是一个闭包,需要捕获其外部范围内任何变量的当前值


这就是为什么
JSLint
想吓唬你不要在一个紧密的循环中创建许多匿名函数。

部分取决于你是在使用函数表达式还是函数声明。它们是不同的东西,发生在不同的时间,对周围的作用域有不同的影响。所以让我们从h.区别

函数表达式是一种
函数
产品,其中使用结果作为右值-例如,将结果指定给变量或属性,或将其作为参数传递给函数等。这些都是函数表达式:

(不要使用最后一个-称为命名函数表达式-实现有bug,)

相反,这是一个函数声明:

它是独立的,您没有将结果用作右侧值

它们之间的两个主要区别是:

  • 函数表达式在程序流中遇到时进行求值。声明在控件进入包含范围(例如,包含函数或全局范围)时进行求值

  • 函数名(如果有)是在函数声明的包含范围中定义的。它不是函数表达式的名称(禁止浏览器错误)

  • 匿名函数是函数表达式,因此禁止解释器进行优化(这是免费的),它们将在每个循环中重新创建。因此,如果您认为实现会优化,那么您可以使用它,但将其分解为命名函数还有其他好处,而且更重要的是,不会花费任何成本。此外,请参阅,了解解释程序可能无法在每次迭代中优化并重新创建函数的原因,具体取决于h它检查代码的深度有多深

    更大的问题是,如果在循环、条件体等中使用函数声明:

    function foo() {
        for (i = 0; i < limit; ++i) {
            function bar() { ... } // <== Don't do this
            bar();
        }
    }
    
    函数foo(){
    对于(i=0;ifunction bar(){…}//Boo to JSLint。它就像头上的钝器。每次遇到
    函数
    时都会创建一个新的函数对象(它是一个语句/表达式,而不是声明--编辑:这是一个善意的谎言。请参见T.J.Crowders的答案)通常这是在一个闭包循环中完成的。更大的问题是创建错误的闭包

    例如:

    for (var i = 0; i < 10; i++) {
      setTimeout(function () {
        alert(i)
      }, 10)
    }
    
    for(变量i=0;i<10;i++){
    setTimeout(函数(){
    警报(一)
    }, 10)
    }
    
    这不是“在循环中创建函数,甚至不理解JS用于变量作用域和闭包的规则”(变量不绑定在闭包中,作用域——执行上下文——是)的问题

    但是,您可能希望在函数中创建一个闭包。请考虑这个不太令人惊讶的代码:

    for (var i = 0; i < 10; i++) {
      setTimeout((function (_i) { 
        return function () {
          alert(_i)
        }
      })(i), 10)
    }
    
    for(变量i=0;i<10;i++){
    setTimeout((函数(_i){
    返回函数(){
    警报(_i)
    }
    })(i) ,10)
    }
    
    哦,不!我还是创建了一个函数

    Javascript解释器真的会在每次迭代中创建函数的实例吗

    它必须这样做,因为它不知道函数对象是否会在其他地方被修改。请记住,函数是标准JavaScript对象,因此它们可以具有与任何其他对象一样的属性。执行此操作时:

    card = $('<div>').bind('isPopulated', function (ev) { ... })
    

    显然,如果在所有迭代中共享函数对象,这将导致错误的行为。

    @ CasabaChana:如果在规范中读取语法,则是无效的。考虑条件句:特别重要的是:<代码>(a){函数英尺(){{}}} {函数FULL(){{}}。
    。这就是为什么如果你阅读语法,它是无效的;-)对不起,我刚刚检查了规范,你是对的。但在我看来,如果语法允许,它的行为方式将与
    如果(a){var b;}否则{var c;}
    与函数开始时声明的内容相同。@T.J.Crowder:你说得对,我刚刚检查了规范。
    FunctionBody
    包含
    SourceElements
    ,其中包含
    SourceElement
    s,其中既包含
    语句
    s又包含
    FunctionDeclaration
    s@MKSafi: 不,我没有忘记这一点。第一个循环中有一个声明,它是无效的JavaScript(目前)。那里根本不允许有声明。是的,在上一个例子中,
    bar
    函数是在循环之前创建的——这就是我在那里的目的,我说(实际上)“我会这样做的。”。重新表达式与声明:如果它用作右边值(例如,赋值或初始化的右边,或传递到函数的右边,或前面有运算符),则它是一个表达式。如果不是,则它是一个声明。@MKSafi:right.And of c
    for (var i = 0; i < 10; i++) {
      setTimeout(function () {
        alert(i)
      }, 10)
    }
    
    for (var i = 0; i < 10; i++) {
      setTimeout((function (_i) { 
        return function () {
          alert(_i)
        }
      })(i), 10)
    }
    
    card = $('<div>').bind('isPopulated', function (ev) { ... })
    
    function bind(str, fn) {
      fn.foo = str;
    }