CoffeeScript,何时在箭头(->)上使用粗箭头(=>),反之亦然


Answers:


157

不,那不是我要使用的规则。

在定义方法时,我发现胖箭头的主要用例是当您要将方法用作回调并且该方法引用实例字段时:

class A
  constructor: (@msg) ->
  thin: -> alert @msg
  fat:  => alert @msg

x = new A("yo")
x.thin() #alerts "yo"
x.fat()  #alerts "yo"

fn = (callback) -> callback()

fn(x.thin) #alerts "undefined"
fn(x.fat)  #alerts "yo"
fn(-> x.thin()) #alerts "yo"

如您所见,如果不使用粗箭头,将实例的方法的引用作为回调传递给您可能会遇到问题。这是因为this粗箭头将对象的实例绑定到了对象,而细箭头没有将对象绑定到该对象,因此上述称为回调的细箭头方法无法像访问实例字段一样@msg调用其他实例方法。最后一行是使用瘦箭头的情况下的解决方法。


2
如果要使用this从细箭头中调用的,又要使用粗箭头获得的实例变量,该怎么办?
安德鲁·毛

正如我说的那样,“最后一行针对使用细箭头的情况提供了一种解决方法。”
nicolaskruchten 2012年

我想你误解了我的问题。假设回调的默认范围已this设置为我要使用的变量。但是,我也想引用一个类方法,因此我也想引用该类this。我只能在的一个赋值之间进行选择this,那么能够同时使用两个变量的最佳方法是什么?
安德鲁·毛

@AndrewMao您可能应该在本网站上发布完整问题,而不是让我在评论中回答:)
nicolaskruchten 2012年

没关系,问题不是那么重要。但是我只是想澄清一下,这不是您在最后一行代码中所指的内容。
安德鲁·毛

13

在其他答案中没有提到的要注意的一点是,在不需要时用粗箭头绑定功能会导致意外的结果,例如在此示例中,我们将其称为DummyClass。

class DummyClass
    constructor : () ->
    some_function : () ->
        return "some_function"

    other_function : () =>
        return "other_function"

dummy = new DummyClass()
dummy.some_function() == "some_function"     # true
dummy.other_function() == "other_function"   # true

在这种情况下,这些功能确实可以实现预期的功能,并且使用粗箭头似乎没有损失,但是当我们在定义好DummyClass原型后对其进行修改(例如更改某些警报或更改日志的输出)时,会发生什么情况:

DummyClass::some_function = ->
    return "some_new_function"

DummyClass::other_function = ->
    return "other_new_function"

dummy.some_function() == "some_new_function"   # true
dummy.other_function() == "other_new_function" # false
dummy.other_function() == "other_function"     # true

如我们所见,重写我们先前定义的原型函数会导致some_function被正确覆盖,但是other_function在实例上保持不变,因为胖箭头已导致该类中的other_function绑定到所有实例,因此实例不会引用它们的类寻找功能

DummyClass::other_function = =>
    return "new_other_new_function"

dummy.other_function() == "new_other_new_function"    # false

second_dummy = new DummyClass()
second_dummy.other_function() == "new_other_new_function"   # true

即使是粗箭头也不起作用,因为粗箭头只会使函数绑定到新实例(确实获得了新的功能)。

但是,这会导致一些问题,如果我们需要一个可以在所有现有实例(包括事件处理程序)上运行的功能(例如,将日志记录功能切换到输出框等)会怎样?原始定义中的粗箭头],但是我们仍然需要访问事件处理程序中的内部属性[使用粗箭头而不是细箭头的确切原因]。

最简单的方法是在原始类定义中仅包含两个函数,一个函数用细箭头定义,该细箭头执行您希望执行的操作,另一个函数用粗箭头定义,除了调用第一个函数外,什么都不做例如:

class SomeClass
    constructor : () ->
        @data = 0
    _do_something : () ->
        return @data
    do_something : () =>
        @_do_something()

something = new SomeClass()
something.do_something() == 0     # true
event_handler = something.do_something
event_handler() == 0              # true

SomeClass::_do_something = -> return @data + 1

something.do_something() == 1     # true
event_handler() == 1              # true

因此,何时使用瘦/胖箭头可以很容易地通过以下四种方式进行总结:

  1. 当两个条件都满足时,应使用仅细箭头功能:

    • 该方法将永远不会被包括event_handlers在内的引用所传递,例如,您永远不会遇到以下情况:some_reference = some_instance.some_method; some_reference()
    • 并且该方法应该在所有实例上通用,因此,如果原型函数发生更改,则该方法在所有实例上也应通用
  2. 满足以下条件时,应使用单独的粗箭头功能:

    • 该方法应在实例创建时精确地绑定到实例,并且即使函数定义针对原型发生更改也应永久绑定,这包括函数应为事件处理程序且事件处理程序行为应一致的所有情况。
  3. 满足以下条件时,应使用直接调用细箭头功能的粗箭头功能:

    • 该方法需要通过引用(例如事件处理程序)来调用
    • 并且该功能将来可能会通过替换细箭头功能而更改,从而影响现有实例
  4. 满足以下条件时,应使用直接调用粗箭头(未演示)功能的细箭头功能:

    • 胖箭头功能必须始终附加到实例
    • 但是细箭头功能可能会更改(甚至更改为不使用原始粗箭头功能的新功能)
    • 并且不需要通过引用传递细箭头功能

在所有方法中,都必须考虑在可能更改原型函数的情况下,特定实例的行为是否正确运行的情况,例如,尽管函数用粗体箭头定义,但如果调用,其行为在实例中可能不一致在原型中更改的方法


9

通常,->很好。

class Foo
  @static:  -> this
  instance: -> this

alert Foo.static() == Foo # true

obj = new Foo()
alert obj.instance() == obj # true

请注意,静态方法如何返回的类对象,this而实例如何返回的实例对象this

发生的情况是调用语法提供的值this。在此代码中:

foo.bar()

foobar()默认情况下将是函数的上下文。因此,它只是按您想要的方式工作。当您以其他不使用点语法进行调用的方式调用这些函数时,仅需要使用粗箭头。

# Pass in a function reference to be called later
# Then later, its called without the dot syntax, causing `this` to be lost
setTimeout foo.bar, 1000

# Breaking off a function reference will lose it's `this` too.
fn = foo.bar
fn()

在这两种情况下,使用粗箭头声明该功能都可以使它们工作。但是,除非您做一些奇怪的事情,否则通常不需要这样做。

因此,请使用->直到您真正需要时为止,=>并且从不使用=>默认设置。


1
如果您这样做会失败:x = obj.instance; alert x() == obj # false!
nicolaskruchten 2012年

2
当然可以,但这属于“做错了”。现在,我已经编辑了答案,并解释了何时=>需要在类的静态/实例方法上使用a。
亚历克斯·韦恩

Nitpick:// is not a CoffeeScript comment# is a CoffeeScript comment
nicolaskruchten 2012年

如何setTimeout foo.bar, 1000“做错了”?使用胖箭头比使用setTimeout (-> foo.bar()), 1000恕我直言要好得多。
nicolaskruchten 2012年

1
@nicolaskruchten setTimeout当然,该语法中有一个案例。但是您的第一条评论有些人为的,没有揭示合法的用例,只是揭示了它可能会如何破裂。我只是说,=>除非有充分的理由,否则不应该使用a ,尤其是在类实例方法上,因为类实例方法的性能代价是创建需要实例化的新函数。
亚历克斯·韦恩

5

只是理解粗箭头的一个例子

不起作用:(@ canvas未定义)

class Test
  constructor: ->
    @canvas = document.createElement 'canvas'
    window.addEventListener 'resize', ->
      @canvas.width = window.innerWidth
      @canvas.height = window.innerHeight

作品:(@ canvas定义)

class Test
  constructor: ->
    @canvas = document.createElement 'canvas'
    window.addEventListener 'resize', =>
      @canvas.width = window.innerWidth
      @canvas.height = window.innerHeight
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.