命名对象属性中的函数值的目的是什么?

3
var timers = {                                                  //#1

    timerID: 0,                                                   //#2
    timers: [],                                                   //#2

    add: function(fn) {                                           //#3
        this.timers.push(fn);
    },

    start: function runNext() {                                   //#4
        if (this.timerID) return;
        (function() {
            if (timers.timers.length > 0) {
                for (var i = 0; i < timers.timers.length; i++) {
                    if (timers.timers[i]() === false) {
                        timers.timers.splice(i,1);
                        i--;
                    }
                }
                timers.timerID = setTimeout(runNext, 0);
            }
        })();
    },

上面的代码来自John Resig的《JavaScript忍者秘籍》。我不理解的部分是他将一个函数赋给了start的属性,然后将该函数命名为runNext。请问有人能提供一些澄清吗?

2
据我所知,这通常用于调试,特别是在错误堆栈跟踪期间避免匿名函数。 - Joseph
4
为了让一个函数能够调用自身,它需要有一个名字。setTimeout(runNext, 0) 调用引用了这个函数,如果这个函数是完全匿名的,就无法使用该调用。 - Eric Jablow
1
setTimeout中也再次被调用,也许解析本地函数名比解析timers.start更快? - Mike Christensen
1
@Eric Jablow:this.start 怎么样? - zerkms
@Mike Christensen:那听起来不正确。 - zerkms
显示剩余2条评论
2个回答

2

函数的“名称”在特定情况下具有特殊作用,尤其是在FunctionExpression1中使用时非常有用:

x = function theFunc (z) {
    // theFunc is in scope here, and so can be used to refer
    // to the function itself in a recursive manner
    //   (in the posted code it is used with setTimeout)
    return z > 0 ? theFunc(z - 1) * z : 1;
};
// theFunc is NOT in scope here in valid ECMAScript; IE quirks anyone?

x不同,上述的theFunc将始终引用特定的函数对象。如果没有这个名称,则需要使用额外的闭包(或使用this管道)才能递归访问该函数。此外,紧密的绑定使theFunc独立于当前this绑定,这可能是好事或坏事 - 请注意,在调用setTimeout后,theFunc将在不同的绑定环境中调用(这也使得使用this.timerID成为可疑)。
在ECMAScript 3版中,函数名称(标识符)和arguments.callee在范围内将评估为相同的函数对象。但是,在ECMAScript 5版“严格”模式下,arguments.callee无效。
函数名称还可能出现在堆栈跟踪、toString()name/displayName(如已实现)中。
1来自ES5 Annotated,函数声明
引用FunctionExpression中的标识符可以从FunctionExpression的FunctionBody内部,以允许函数递归调用它本身...

所以可以用来递归地引用函数本身,他可以将其称为 this.start() - zerkms
@zerkms 好建议。我增加了一些内容。 - user2246674

-1
在JavaScript中,一切都是具有成员的对象,这些成员可以根据您放置在其中的内容作为属性或方法。
在这种情况下,timer将具有一些将用作方法(addstart)的成员。为了做到这一点,它们将包含对function代码的引用。他之所以给start中的代码引用命名(runNext),仅仅是为了能够从内部递归调用该函数。您在runNext中看到的第二个函数引用是另一个匿名函数引用,甚至没有被分配给任何东西,但它只是在那里使其在那时返回runNext的某些内容。

因为JavaScript程序员喜欢hackiness。是的,他可以把它放在另一个方法中,并通过this调用它。他也可以把那个匿名函数从那里拿出来。 - Francisco Zarabozo
@zerkms this在IIFE内部是未定义的。如果它不是未定义的,在第一次由setTimeout调用函数时,它将变为未定义,因此它将不再递归。 - bfavaretto
@zerkms 为什么要在对象中硬编码路径以递归调用函数,而不是直接引用函数名呢?虽然 OP 的代码是一个简单的例子,但当你只想递归时使用确切的路径可能会很烦人:http://jsfiddle.net/rnaqT/1/ - Ian
1
@zerkms 为什么你不能保持一致,在所有的代码中使用相同的约定呢?如果你需要在对象中递归调用一个函数,那么无论这个对象是多么简单或复杂,都应该始终使用相同的代码。为什么你要称它为棘手的呢?我不明白它有什么不好的地方。 - Ian
1
@zerkms 对,但我想我更指的是像 "asdf".indexOf("s") 这样的东西,但这与你所说的不同,也不像你所说的相关。我同意该语句没有适当或正确地表达出观点。 - Ian
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接