CoffeeScript,何时使用箭头( – >)上的胖箭头(=>),反之亦然
当在CoffeeScript中构build一个类时,是否应该使用=>
(“胖箭头”)运算符定义所有实例方法,并使用->
运算符定义所有静态方法?
不,这不是我会使用的规则。
我在定义方法中使用fat-arrow的主要用例是当你想使用一个方法作为callback,并且该方法引用实例字段时:
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"
正如你看到的,如果你不使用胖箭头,你可能会遇到问题传递一个实例方法的引用作为callback。 这是因为胖箭头将对象的实例绑定到this
而细箭头没有,所以称为callback的细箭头方法不能像@msg
那样访问实例的字段,或者调用其他实例方法。 最后一行有一个解决方法,用于使用细箭头的情况。
在其他答案中没有提及的重要的一点是,在没有必要的情况下使用胖箭头绑定函数可能会导致意外的结果,例如在本例中,我们将调用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原型会发生什么(例如更改某个警报或更改日志的输出) :
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被绑定到所有实例,所以实例不会引用它们的类find一个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
即使胖箭头也不行,因为胖箭头只会导致函数被绑定到新的实例上(这样做会获得新的function)。
然而这会导致一些问题,如果我们需要一个可以在所有现有实例(包括事件处理程序)上工作的函数(例如在将日志函数切换到输出框的情况下),我们不能使用原始定义中的粗箭头],但我们仍然需要访问事件处理程序中的内部属性[我们使用胖箭头而不是细箭头的确切原因]。
那么最简单的方法就是在原始类定义中只包含两个函数,一个用细箭头定义,你可以执行这个操作,另一个用一个胖箭头定义,除了调用第一个函数例如:
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
所以何时使用瘦/胖箭头可以用四种方式相当简单的总结:
-
当这两个条件都符合时,应该使用细箭头函数:
- 该方法将永远不会通过引用传递,包括event_handlers,例如,你永远不会有这样的情况:some_reference = some_instance.some_method; some_reference()
- 而且该方法应该在所有实例中都是通用的,所以如果原型函数发生变化,那么方法就会覆盖所有实例
-
当满足以下条件时,应该使用胖箭头function:
- 该方法应该与创build实例时的实例绑定,并且即使原型的函数定义发生更改也会永久保持绑定,这包括函数应该是事件处理程序和事件处理程序行为应该一致的所有情况
-
当满足以下条件时,应使用直接调用细箭头function的胖箭头function:
- 该方法需要通过引用(如事件处理程序)进行调用
- 并且function可能会在将来通过replace细线箭头function而影响现有实例
-
当满足以下条件时,应使用直接调用胖箭头(未显示)function的细箭头function:
- 胖箭头function必须始终附加到实例
- 但细箭头function可能会改变(甚至不使用原始的胖箭头function的新function)
- 而细箭头function从不需要通过引用传递
在所有的方法中,必须考虑在原型函数可能被改变的情况下,无论特定实例的行为是否将正确行为,例如,虽然函数是用胖箭头来定义的,但是如果它调用的话,它的行为在实例中可能不一致在原型中改变的方法
通常, ->
很好。
class Foo @static: -> this instance: -> this alert Foo.static() == Foo # true obj = new Foo() alert obj.instance() == obj # true
请注意,静态方法如何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()
在这两种情况下,使用胖箭头声明该function将允许这些工作。 但是除非你做的事情有点奇怪,你通常不需要。
所以使用->
直到你真的需要=>
,从来没有使用=>
默认情况下。
只是一个例子,为了坚持脂肪箭头
不起作用:(@canvas undefined)
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