在CoffeeScript中构建类时,是否应该使用=>
(“ fat arrow”)运算符定义所有实例方法,并使用该->
运算符定义所有静态方法?
在CoffeeScript中构建类时,是否应该使用=>
(“ fat arrow”)运算符定义所有实例方法,并使用该->
运算符定义所有静态方法?
Answers:
不,那不是我要使用的规则。
在定义方法时,我发现胖箭头的主要用例是当您要将方法用作回调并且该方法引用实例字段时:
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
调用其他实例方法。最后一行是使用瘦箭头的情况下的解决方法。
this
从细箭头中调用的,又要使用粗箭头获得的实例变量,该怎么办?
this
设置为我要使用的变量。但是,我也想引用一个类方法,因此我也想引用该类this
。我只能在的一个赋值之间进行选择this
,那么能够同时使用两个变量的最佳方法是什么?
在其他答案中没有提到的要注意的一点是,在不需要时用粗箭头绑定功能会导致意外的结果,例如在此示例中,我们将其称为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
因此,何时使用瘦/胖箭头可以很容易地通过以下四种方式进行总结:
当两个条件都满足时,应使用仅细箭头功能:
满足以下条件时,应使用单独的粗箭头功能:
满足以下条件时,应使用直接调用细箭头功能的粗箭头功能:
满足以下条件时,应使用直接调用粗箭头(未演示)功能的细箭头功能:
在所有方法中,都必须考虑在可能更改原型函数的情况下,特定实例的行为是否正确运行的情况,例如,尽管函数用粗体箭头定义,但如果调用,其行为在实例中可能不一致在原型中更改的方法
通常,->
很好。
class Foo
@static: -> this
instance: -> this
alert Foo.static() == Foo # true
obj = new Foo()
alert obj.instance() == obj # true
请注意,静态方法如何返回的类对象,this
而实例如何返回的实例对象this
。
发生的情况是调用语法提供的值this
。在此代码中:
foo.bar()
foo
bar()
默认情况下将是函数的上下文。因此,它只是按您想要的方式工作。当您以其他不使用点语法进行调用的方式调用这些函数时,仅需要使用粗箭头。
# 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()
在这两种情况下,使用粗箭头声明该功能都可以使它们工作。但是,除非您做一些奇怪的事情,否则通常不需要这样做。
因此,请使用->
直到您真正需要时为止,=>
并且从不使用=>
默认设置。
x = obj.instance; alert x() == obj # false!
=>
需要在类的静态/实例方法上使用a。
// is not a CoffeeScript comment
而# is a CoffeeScript comment
。
setTimeout foo.bar, 1000
“做错了”?使用胖箭头比使用setTimeout (-> foo.bar()), 1000
恕我直言要好得多。
setTimeout
当然,该语法中有一个案例。但是您的第一条评论有些人为的,没有揭示合法的用例,只是揭示了它可能会如何破裂。我只是说,=>
除非有充分的理由,否则不应该使用a ,尤其是在类实例方法上,因为类实例方法的性能代价是创建需要实例化的新函数。
只是理解粗箭头的一个例子
不起作用:(@ 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