这两个实现有什么不同

What's the different between these two implementations

代码段 A:

DemoFilter =
  onConfirmed: (cb) ->
    cb()
a =
  onConfirmed: (callback) ->
    this.callback = callback
  confirm: ->
    this.callback()

b =
  init: ->
    a.onConfirmed =>
       DemoFilter.onConfirmed @mycallback
  mycallback: =>
    console.log this # output: {} or Object window on browser

b.init()
a.confirm()

代码段 B:

DemoFilter =
  onConfirmed: (cb) ->
    cb()
a =
  onConfirmed: (callback) ->
    this.callback = callback
  confirm: ->
    this.callback()

b =
  init: ->
    a.onConfirmed =>
       DemoFilter.onConfirmed =>
         console.log this # output: Object b
  # mycallback: =>
  #   console.log this

b.init()
a.confirm()

为什么这些console.log输出不一样?

在节点环境中:

A:输出{}
B:输出Object b

我发现编译结果完全一样,我想不通为什么结果不一样。

您的问题是您使用的是简单对象而不是 classes,因此 => 的行为方式与您期望的不同。当你这样说时:

o =
  m: =>

这和说的完全一样:

f = =>
o =
  m: f

这意味着 m 中的 @ (AKA this) 是全局对象(浏览器中的 window 或者,AFAIK,node.js) 而不是您期望的 o

当你说:

class C
  m: =>

当您说 o = new C 时,CoffeeScript 会将 m 绑定到 C 实例 。如果您使用普通对象文字而不是 class,则没有特殊的构造阶段来设置绑定,也没有 => 的 class 实例将函数绑定到。

如果我们回到你的第一个案例:

b =
  init: ->
    a.onConfirmed =>
       DemoFilter.onConfirmed @mycallback
  mycallback: =>
    console.log this

mycallback 将绑定到任何全局对象。此外,@(又名 this)的值取决于函数的调用方式而不是函数的定义位置(当然不包括绑定函数),所以如果你说:

b.init()

然后 init 内的 @ 将是 b。然后你交给的匿名函数a.onConfirmed:

a.onConfirmed =>
   DemoFilter.onConfirmed @mycallback

将绑定到 b,因为当您定义该函数时 @b。但是 mycallback 不会在意,因为它已经绑定到全局对象。

第二种情况:

b =
  init: ->
    a.onConfirmed =>
       DemoFilter.onConfirmed =>
         console.log this

当调用 init 时,我们再次将 @ 变为 b,并且传递给 a.onConfirmed 的匿名函数将再次绑定到 b。这意味着当:

DemoFilter.onConfirmed => ...

被调用,@ 将再次成为 b。这里我们有另一个匿名绑定函数,因为 @b 在这个级别我们有 this (又名 @)当 console.log this 时是 b被调用。

如果您使用 classes 而不仅仅是对象:

class A
  onConfirmed: (@callback) ->
  confirm: ->
    @callback()

class B
  constructor: (a) ->
    a.onConfirmed =>
       DemoFilter.onConfirmed @mycallback
  mycallback: =>
    console.log @

a = new A
b = new B(a)
a.confirm()

那么您应该会看到预期的行为。