0

我想创建一个类,它的方法可以被子类覆盖,但也可以用作回调。看来我只能在一种或另一种情况下获得所需的行为。这是一个例子:

class Parent
    constructor: () ->
        @foo = "foo"

    fooNotAlwaysDefined: () ->
        console.log("In parent.fooNotAlwaysDefined, @foo:#{@foo}")

    childNotCalled: () =>
        console.log("In parent.childNotCalled, @foo:#{@foo}")

class Child extends Parent
    fooNotAlwaysDefined: () ->
        console.log("In child.fooNotAlwaysDefined, @foo:#{@foo}")

    childNotCalled: () ->
        console.log("In child.childNotCalled, @foo:#{@foo}")

c = new Child()
c.fooNotAlwaysDefined()
c.childNotCalled()
process.nextTick(c.fooNotAlwaysDefined)
process.nextTick(c.childNotCalled)

我想要的是调用子函数并且 @foo 在两种用途中都在范围内(c。和作为回调)。这是我得到的输出:

在 child.fooNotAlwaysDefined 中,@foo:foo

在 parent.childNotCalled 中,@foo:foo

在 child.fooNotAlwaysDefined 中,@foo:undefined

在 parent.childNotCalled 中,@foo:foo

我发现的最好的解决方法是我可以将 fooNotAlwaysDefined 包装在一个提供给 process.nextTick 的匿名函数中,但这并不理想。

process.nextTick(() -> c.fooNotAlwaysDefined())

在 child.fooNotAlwaysDefined 中,@foo:foo

有没有办法构造类,以便我得到我想要的行为?

编辑:回答:下面非常有用的评论的摘要是 childNotCalled 看到的行为是一个错误。我会注意到我在 1.6.1 中看到了这种行为,所以虽然它可能已经得到改进,但它并没有解决这个问题。

第二次编辑:问题似乎已在 1.6.2 中完全解决。

4

2 回答 2

2

这是this没有正确绑定的经典问题。除了您已经找到的解决方案之外,还有两种可能的解决方案:

您也可以childNotCalled使用粗箭头进行定义。

嗯,有点恶心。粗箭头方法是一种代码味道,因为它们“通常有效”但允许你偷懒而不去考虑this. 更清洁的方式:

process.nextTick child.fooNotAlwaysDefined.bind(child)

大致相当于将其包装为一个匿名函数,但更具声明性,即使您将child变量重新分配给其他东西也可以工作。fooNotAlwaysDefined它返回一个带有this绑定到的新实例child,因此当它在下一个滴答时执行时,它将具有正确的this.

这与用粗箭头声明方法的做法非常相似,只是让它更明确、更高效、更容易理解(因为你可以从那行代码中看到它在做正确的事情,而不是必须检查方法的定义以确保正确性)。

超类中的胖箭头将覆盖子方法的实现,除非他们也使用胖箭头,这一事实使它们对我没有吸引力(检查编译的代码以了解为什么会这样。这是唯一的方法,并且没有办法绕过它(除了禁止胖箭头方法),但这不是你所期望的)。这是 CoffeeScript 的一个特性,我觉得没有它会更好。

于 2013-03-09T02:21:15.893 回答
0

这正是 Underscore 的bindAll方法所针对的情况。它将一组方法绑定到this,以便它们可以用作回调。此外,它与继承配合得很好,这在 CoffeeScript 的粗箭头语法中似乎失败了。

http://underscorejs.org/#bindAll

如果您不想使用 Underscore 或 LoDash,您可以通过在Parent的构造函数中添加以下内容来自己完成:

for methodName in ['fooNotAlwaysDefined', 'childNotCalled']
    @[methodName] = do(method = @[methodName]) =>
        => method.apply @, arguments

使用下划线的bindAll方法更干净:

_.bindAll @, 'fooNotAlwaysDefined', 'childNotCalled'

或者,如果您不担心与旧浏览器的兼容性(好像您在 Node 中运行),您可以假设函数具有内置bind方法:

@fooNotAlwaysDefined = @fooNotAlwaysDefined.bind @
@childNotCalled = @childNotCalled.bind @
于 2013-03-09T02:49:03.997 回答