我想创建一个类,该方法可以被子类覆盖,但也可以用作回调。似乎我只能在一种或另一种情况下获得所需的行为。这是一个例子:
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中完全解决。
答案 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 @