如何将一个方法都覆盖并用作coffeescript中的回调

时间:2013-03-09 01:52:23

标签: inheritance methods callback coffeescript

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

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中完全解决。

2 个答案:

答案 0 :(得分:2)

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

您也可以使用胖箭头定义childNotCalled

嗯,有点严重。脂肪箭头方法是一种代码气味,因为它们“通常起作用”但允许你变得懒惰而不考虑this的价值。更清洁的方式:

process.nextTick child.fooNotAlwaysDefined.bind(child)

大致相当于将它包装成一个匿名函数,但更具说明性,即使你将child变量重新分配给其他东西也会有效。它返回一个fooNotAlwaysDefined的新实例,this绑定到child,这样当它在下一个刻度执行时,它将具有正确的this

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

超类中的胖箭头会覆盖子方法的实现,除非它们也使用胖箭头,这是另一件让它对我不具吸引力的事实(查看编译后的代码,看看为什么会这样。这是唯一的方法做它并且没有办法解决它(除了禁止胖箭头方法),但它不是你所期望的)。这是CoffeeScript的一个功能我觉得没有它会更好。

答案 1 :(得分:0)

这正是Underscore的bindAll方法的设计目标。它将一组方法绑定到this,以便它们可以用作回调。此外,它可以很好地继承,这似乎是使用CoffeeScript的胖箭头语法失败的。

http://underscorejs.org/#bindAll

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

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

使用Underscore的bindAll方法更简洁:

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

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

@fooNotAlwaysDefined = @fooNotAlwaysDefined.bind @
@childNotCalled = @childNotCalled.bind @