当在coffeescript中构建类时,有没有理由不使用胖箭头的实例方法? [英] When building classes in coffeescript, is there ever a reason to not use the fat arrow for the instance methods?

查看:239
本文介绍了当在coffeescript中构建类时,有没有理由不使用胖箭头的实例方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在coffeescript中构建类时,是否有理由使用胖箭头作为实例方法?



编辑:
那么!很好的回复! :)
总而言之,问题是:

- 花费更多的内存
- 无法修补 -
- Begs问题,为什么它用于这种方法?

惯例:

- 在绑定函数时显式。

- 在构造函数中声明fat arrowed方法。

- 尽可能多地使用,而不是在类声明中。

解决方案

是的,有理由不要总是使用胖箭头。事实上,我赞成永远不要使用fat-arrowed方法:)



薄箭和胖箭方法在概念上是不同的东西。前者被编译为预期的基于原型的JS代码;该方法属于类原型。 fat-arrowed方法,另一方面与构造函数代码中的每个实例相关联。



总是使用fat-arrowed方法的最明显的缺点是它使每个类实例需要更多的内存(因为它拥有更多的属性),它的初始化速度更慢(因为它必须创建这些绑定的函数并在每次创建实例时设置它们)。



使用fat-arrow方法的另一个缺点是它破坏了一个方法的通常期望:一个方法不再是一个类的实例之间共享的函数,但是它现在是每个实例的一个单独的函数。这可能会导致问题,例如,您想在类中定义方法后修改方法:

  class Foo 
#使用fat-arrow方法
bar:(x)=> alert x

#我有一些Foos
foos =(new Foo for i in [1..3])

#我想路径的bar方法添加一些日志记录。
#这可能在另一个模块或文件中。
oldbar = Foo :: bar
Foo :: bar =(args ...) - >
console.logFoo :: bar call,args
oldbar.apply @,args

#console.log永远不会被调用,因为bar方法
#已经绑定到每个实例,并且没有被
#上面的补丁修改。
foo.bar(i)for foo,i in foos





b $ b

但我认为最重要的缺点是更主观:引入fat-arrow方法使代码(和语言)不必要地不一致和难以理解。



代码变得更加不一致,因为在引入fat-arrow方法之前,我们看到< someProp> ;:< someVal> 在类定义中我们知道这意味着声明一个名为的< someProp>的属性,值为< someVal> 在类原型中(除非< someProp> =='constructor',这是一种特殊情况) $ c>< someVal> 是一个数字或一个函数,它只是原型中的一个属性。随着fat-arrowed方法的引入,我们现在有了一个不必要的特殊情况:如果< someVal> 是一个胖箭头的函数,其他值。



还有另一个不一致:胖箭头 bind当在方法定义中使用 this 时,与在其他地方使用时不同。而不是保留外部(其中 this 绑定到类构造函数)在一个胖箭头方法内的 this 是一个对象,当该方法被定义时不存在(即类的实例)。



如果你混合了细箭头和胖箭头的方法,代码也变得难以跟随,因为现在每当开发人员看到一个胖箭头的方法问自己为什么需要 方法是实例绑定的。在方法的声明和它被使用的地方之间没有立即的相关性,这就是fat-arrow方法的需要。






对于所有这些,我建议不要使用fat-arrow方法。希望将方法绑定到要使用它的实例,而不是声明方法的地方。例如:

 #明确关于'someClick'调用'onClick':
$ someJQueryElement.on'点击,(e) - > someObject.onClick e

#而不是:
$ someJQueryElement.on'click',someObject.onClick

或者,如果你真的想在构建时绑定每个实例上的方法,请明确说明:

 #而不是fat-arrow方法:
class A
constructor: - >
@bar = 42
foo:=>
console.log @bar

#在构造函数中使用方法,就像
#do和任何其他属性一样
class A
构造函数:>
@bar = 42
@foo = =>
console.log @bar

我认为在<$ c $的第二个定义c> class A 它比第一个定义更清楚地显示了 foo 方法发生了什么。



最后,请注意,我并不反对使用fat箭头。这是一个非常有用的结构,我一直使用它的正常功能;我只是希望避免在方法定义中使用它)。






修改:另一个使用fat-arrow方法的情况:装饰函数:

 #A装饰器功能配置另一个功能。 
profiled =(fn) - >
(args ...) - >
console.profile()
fn.apply @,args
console.profileEnd()

class A
bar:10

#这是正常工作
foo:profiled(baz) - >
console.log@bar + baz:,@bar + baz

#这不是
fatArrowedFoo:profiled(baz)=>
console.log@bar + baz:,@bar + baz

(new A).foo 5# - > @bar + baz:15
(new A).fatArrowedFoo 5# - > @bar + baz:NaN


When building classes in coffeescript, is there ever a reason to not use the fat arrow for instance methods?

Edit: 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 :)

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.

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.

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).

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

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.

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中构建类时,有没有理由不使用胖箭头的实例方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆