问题描述
在用 coffeescript 构建类时,是否有理由不将粗箭头用于实例方法?
When building classes in coffeescript, is there ever a reason to not use the fat arrow for instance methods?
好吧!很棒的回复!:)
总结一下,问题是:
- 占用更多内存
- 无法修补
- 提出问题,为什么将它用于这种方法?
约定:
- 绑定函数时要明确.
- 在构造函数中声明箭头方法.
- 尽可能多地使用,只是不在类声明中.
Ok then! Great reply! :)
To sum up, the problems are:
- Takes more memory
- Inability to patch
- Begs question, why is it used for this method?
Convention:
- Be explicit when binding functions.
- Declare fat arrowed methods in the constructor.
- Use as much as you want, just not in class declarations.
推荐答案
是的,有理由不总是使用粗箭头.事实上,我赞成从不使用粗箭头方法:)
Yes, there are reasons to not use the fat arrows always. In fact i'd argue in favour of never using fat-arrowed methods :)
细箭头和粗箭头方法在概念上是不同的.前者被编译为预期的基于原型的 JS 代码;方法属于类原型.另一方面,粗箭头方法与构造函数代码中的每个实例相关联.
Thin-arrow and fat-arrow methods are conceptually different things. The former are compiled to the expected prototype-based JS code; the methods belong to the class prototype. Fat-arrowed methods, on the other hand are associated with each instance in the constructor's code.
总是使用粗箭头方法最明显的缺点是它使每个类实例占用更多内存(因为它有更多自己的属性)并且它的初始化速度更慢(因为它必须创建那些绑定函数并将它们设置为每个创建实例的时间).
The most obvious disadvantage of always using fat-arrowed methods is that it makes each class instance take more memory (because it has more own properties) and its initialization be slower (because it has to create those bound functions and set them each time an instance is created).
使用粗箭头方法的另一个缺点是它打破了通常对方法的期望:方法不再是类实例之间共享的函数,而是每个实例的单独函数.这可能会导致问题,例如,如果您想在类中定义方法后对其进行修改:
Another disadvantage of using fat-arrow methods is that it breaks the usual expectation of what a method is: a method is no longer a function shared between the instances of a class, but it now is a separate function for each instance. This can cause problems if, for example, you'd want to modify a method after it has been defined in the class:
class Foo
# Using fat-arrow method
bar: (x) => alert x
# I have some Foos
foos = (new Foo for i in [1..3])
# And i want to path the bar method to add some logging.
# This might be in another module or file entirely.
oldbar = Foo::bar
Foo::bar = (args...) ->
console.log "Foo::bar called with", args
oldbar.apply @, args
# The console.log will never be called here because the bar method
# has already been bound to each instance and was not modified by
# the above's patch.
foo.bar(i) for foo, i in foos
但在我看来,最重要的缺点是更加主观:引入粗箭头方法会使代码(和语言)不必要地不一致且难以理解.
But the most important disadvantage in my opinion is more subjective: introducing fat-arrow methods makes the code (and the language) unnecessarily inconsistent and difficult to understand.
代码变得更加不一致,因为在引入粗箭头方法之前,每当我们在类定义中看到 <someProp>: <someVal>
我们知道这意味着声明一个名为 <someProp>
在类的原型中带有值 "(除非
== 'constructor'
,即一个特例), 是数字还是函数都没有关系,它只是原型中的一个属性.随着粗箭头方法的引入,我们现在有了另一个不必要的特殊情况:如果
是一个粗箭头函数,它将做与任何其他值完全不同的事情.
The code becomes more inconsistent because before introducing fat-arrow methods any time we see <someProp>: <someVal>
in a class definition we know it means "declare a property named <someProp>
with a value <someVal>
in the class' prototype" (unless <someProp> == 'constructor'
, which is a special case), it doesn't matter if <someVal>
is a number or a function, it will just be a property in the prototype. With the introduction of fat-arrowed methods we now have another unnecessary special case: if <someVal>
is a fat-arrowed function it will do a completely different thing than with any other value.
还有另一个不一致之处:粗箭头以不同的方式绑定this
当它们在方法定义中使用时,而不是在其他任何地方使用时.而不是保留外部 this
(在 class
中,this
绑定到类构造函数)this
> 粗箭头方法内部是定义方法时不存在的对象(即类的实例).
And there's another inconsistency: fat arrows bind the this
differently when they are used in a method definition than when used anywhere else. Instead of preserving the outer this
(which, inside a class
, this
is bound to the class constructor) the this
inside a fat-arrowed method is an object that does not exist when the method is defined (i.e. an instance of the class).
如果混合使用细箭头和粗箭头方法,代码也会变得更难理解,因为现在每次开发人员看到粗箭头方法时,他们都会问自己为什么需要它em> 方法被实例绑定.方法的声明和使用它的地方之间没有直接的相关性,这就是需要粗箭头方法的地方.
If you mix thin-arrowed and fat-arrowed methods the code also becomes harder to follow because now every time a developer sees a fat-arrowed method they'll ask themselves why was it needed that for that method to be instance-bound. There's no immediate correlation between the method's declaration and where it's being used, which is where the need for fat-arrow method arises.
对于所有这些,我建议永远不要使用粗箭头方法.更喜欢将方法绑定到将要使用它的实例,而不是在声明方法的地方.例如:
For all this, i'd recommend to never use fat-arrow methods. Prefer binding the method to an instance where it's going to be used instead of where the method is declared. For example:
# Be explicit about 'onClick' being called on 'someObject':
$someJQueryElement.on 'click', (e) -> someObject.onClick e
# Instead of:
$someJQueryElement.on 'click', someObject.onClick
或者,如果您真的想在构建时将方法绑定到每个实例上,请明确说明:
Or, if you really want to bind the method on every instance on construction time, be explicit about that:
# Instead of fat-arrow methods:
class A
constructor: ->
@bar = 42
foo: =>
console.log @bar
# Assing the method in the constructor, just like you would
# do with any other own property
class A
constructor: ->
@bar = 42
@foo = =>
console.log @bar
我认为在 class A
的第二个定义中,foo
方法发生的事情比第一个定义中的要明确得多.
I think that in the second definition of class A
it's much more explicit what is happening with the foo
method than in the first definition.
最后,请注意,我根本不反对使用粗箭头.这是一个非常有用的构造,我一直将它用于正常功能;我只是更喜欢避免在 class
方法定义中使用它:)
Finally, notice that i'm not arguing against using the fat arrow at all. It's a very useful construct and i use it all the time for normal functions; i just prefer to avoid using it inside a class
method definition :)
编辑:另一种反对使用粗箭头方法的情况:装饰器函数:
Edit: Another case against using fat-arrowed methods: decorator functions:
# A decorator function to profile another function.
profiled = (fn) ->
(args...) ->
console.profile()
fn.apply @, args
console.profileEnd()
class A
bar: 10
# This works as expected
foo: profiled (baz) ->
console.log "@bar + baz:", @bar + baz
# This doesn't
fatArrowedFoo: profiled (baz) =>
console.log "@bar + baz:", @bar + baz
(new A).foo 5 # -> @bar + baz: 15
(new A).fatArrowedFoo 5 # -> @bar + baz: NaN
这篇关于在coffeescript 中构建类时,是否有理由不对实例方法使用粗箭头?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!