Javascript: 构造函数中的回调函数

6

我正在尝试编写面向对象的JavaScript代码,针对一个具有昂贵初始化过程的对象,该过程将在完成时回调函数。

问题是调用者需要在回调例程中使用该对象的函数,而该对象尚不存在:

// ctor for foo object
function foo(callback) {

    // do slow initialization here..

    // callback when done
    callback();
};

foo.prototype = function() {

    return {
        // doStuff method
        doStuff: function() {
          alert('stuff done');
        }
    };        
}();

// instantiate the foo object, passing in the callback
var f = new foo(function() {

    //Uncaught TypeError: Cannot call method 'doStuff' of undefined 
    f.doStuff();

});​

jsFiddle 这里我错过了什么?


我不明白为什么你需要一个回调函数来完成同步的函数,而且回调函数并不返回有用的值。 - icktoofay
1
“慢初始化”实际上涉及到一个第三方库,该库期望传递回调函数。 - saille
3个回答

7

这应该是一个简单的修复。首先,请确保使用回调函数时将this对象设置为当前对象。

function foo(callback) {
    // do slow initialization here..

    callback.call(this);
};

然后调整回调的方式

var f = new foo(function() {
    this.doStuff();
});​

这是您的更新后的代码片段

问题是通过不引用对象,而是使用“this”关键字解析当前上下文来解决的。 - Joe Johnson
1
虽然 OP 的方法听起来很奇怪,但这可能是最好的方法,OP 只是忘记允许从回调中访问实例。我更喜欢将 this 作为参数传递给回调函数,不过这并不重要,只是个人偏好。 - Ruan Mendes

2

你的方法有内在问题。在构造函数内定义doStuff。调用方式如下:

var f = new Foo();
f.doStuff();

你还可以创建一个初始化方法,每当创建一个新对象时运行。但是,你应该在构造函数的范围内完成这个操作……不要将回调传递给构造函数。


Adam Rackis 提供了一个非常好的替代方案。但是,我仍然不建议通过传递回调来实例化对象...除非您每次都想传递不同的函数。无论哪种方式,都可能难以维护该代码(在较大的应用程序中)。 - Joe Johnson
1
我倾向于同意。传递回调指令对象调用自己的方法对我来说似乎有些可疑... - Adam Rackis
它只是将逻辑封装到对象中,这些对象可能更适合于“事物”与“执行操作”的心理模型。不过看起来有点复杂。 - Zach Smith

2
这是为什么它不起作用的原因:当JavaScript执行代码时,它需要评估new foo(...)表达式,然后才能设置f。在构造函数中调用回调函数时,JavaScript仍未设置f,因为该表达式尚未完成。一旦构造函数完成,f就会被正确地设置,但当你在回调函数中使用f时,它仍然是undefined,因此无法实现预期效果。
您可以在事件循环的下一次迭代中调用回调函数,而不是像这样:
callback();

你需要做的是:

setTimeout(callback, 0);

这只有在你已经假定它是异步完成的情况下才能起作用。

这个在Node中对我起作用了。被接受的答案在Node中没有起作用。 - joelc
如果你正在使用Node,你可能想考虑使用process.nextTick(callback) - icktoofay
process.nextTick(callback) 没有调用回调函数。 - joelc

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