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

nic*_*ten 3 events event-handling javascript-events 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()
Run Code Online (Sandbox Code Playgroud)

这是输出:

test1
fire 1
fire 2

test2
fire 2
hey there

test3
fire 1
fire 2
hey there
Run Code Online (Sandbox Code Playgroud)

Eri*_*ris 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()
Run Code Online (Sandbox Code Playgroud)

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

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

...

emitter1.on "test1", bindMethod(emitter2, 'go2')
emitter2.on "test2", bindMethod(handler, 'handle')
Run Code Online (Sandbox Code Playgroud)

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