为什么我不能委托事件处理程序2深入?

时间:2011-03-30 22:25:31

标签: events javascript-events event-handling node.js coffeescript

我在CoffeeScript / NodeJS中看到一些关于EventEmitters和处理程序的非常奇怪的行为。我把一个展示问题的小样本放在一起......

基本上我在事件处理中有一些间接,但我似乎无法让它工作,除非我将第一个事件处理程序包装在lambda中,并且我想了解为什么/如果有什么我可以做的事情做这个工作。从我的思维方式来看,基本上test1()应该与test3()具有相同的行为。包含test2()只是为了表明第二级事件处理有效!

events = require "events"

class ExampleEmitter extends events.EventEmitter
    constructor: () ->
    go1: () -> 
        console.log("fire 1")
        @emit("test1", "there")
    go2: () -> 
        console.log("fire 2")
        @emit("test2", "there")

class ExampleHandler
    constructor: () ->
    handle: (x) -> console.log("hey", x)

test1 = () ->        
    handler  = new ExampleHandler()
    emitter1 = new ExampleEmitter()
    emitter2 = new ExampleEmitter()
    emitter1.on "test1", emitter2.go2
    emitter2.on "test2", handler.handle #this doesn't fire :(
    emitter1.go1()

test2 = () ->        
    handler  = new ExampleHandler()
    emitter1 = new ExampleEmitter()
    emitter2 = new ExampleEmitter()
    emitter1.on "test1", emitter2.go2
    emitter2.on "test2", handler.handle
    emitter2.go2()

test3 = () ->        
    handler  = new ExampleHandler()
    emitter1 = new ExampleEmitter()
    emitter2 = new ExampleEmitter()
    emitter1.on "test1", () -> emitter2.go2() #why must I wrap this?
    emitter2.on "test2", handler.handle
    emitter1.go1()

console.log "\ntest1"
test1()
console.log "\ntest2"
test2()
console.log "\ntest3"
test3()

这是输出:

test1
fire 1
fire 2

test2
fire 2
hey there

test3
fire 1
fire 2
hey there

2 个答案:

答案 0 :(得分:5)

  

emitter1.on "test1", () -> emitter2.go2() #why must I wrap this?

因为如果你只是传递emitter2.go2,go2将在根对象(浏览器中为window;我不太了解node.js)的上下文中调用,而不是emitter2。一个函数本身并不知道它所属的对象。实际上,您应该将关闭传递给on的两个调用。

为了使事情看起来更好一点,如果你的闭包不带任何参数,你可以省略括号。最终,你应该有这样的东西:

handler  = new ExampleHandler()
emitter1 = new ExampleEmitter()
emitter2 = new ExampleEmitter()
emitter1.on "test1", -> emitter2.go2()
emitter2.on "test2", -> handler.handle()
emitter1.go1()

如果你仍然不喜欢它的外观,那么下一个最好的方法是使用一个函数,通过创建这样的闭包将函数“绑定”到对象。但是,它不会为你节省任何打字,我觉得它看起来很丑陋而且难以理解:

bindMethod = (obj, funcName) ->
    -> obj[funcName].apply(obj, arguments)

...

emitter1.on "test1", bindMethod(emitter2, 'go2')
emitter2.on "test2", bindMethod(handler, 'handle')

最后,您可以使用胖箭头=>在类声明中创建此类绑定方法,以便您可以根据需要传递它们。 go2: -> ...将成为go2: => ...,& c。但在这种情况下,我认为这是一种奇怪的行为。我坚持传递闭包,因为它使意义更清晰。

答案 1 :(得分:1)

我会在这里添加另一个答案,即使我已经接受了上面的答案,以防人们不阅读评论......

我的问题的实际修复,是为了获得我正在寻找的行为,是在我的课程定义中使用=>'胖箭',而不是普通的->'细箭头'像我一样,将函数绑定到类的实例。所以:

class ExampleEmitter extends events.EventEmitter
    constructor: () ->
    go1: () => 
        console.log("fire 1")
        @emit("test1", "there")
    go2: () => 
        console.log("fire 2")
        @emit("test2", "there")