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

发布于 2024-12-28 09:53:43 字数 118 浏览 1 评论 0原文

在 CoffeeScript 中构建类时,是否应使用 =>(“粗箭头”)运算符定义所有实例方法,并使用 ->-> 定义所有静态方法代码> 运算符?

When building a class in CoffeeScript, should all the instance method be defined using the => ("fat arrow") operator and all the static methods being defined using the -> operator?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

╰沐子 2025-01-04 09:53:43

不,这不是我会使用的规则。

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

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 而细箭头则不然,因此如上所述作为回调调用的细箭头方法无法访问实例的字段,例如 < code>@msg 或调用其他实例方法。最后一行有一个针对使用细箭头的情况的解决方法。

No, that's not the rule I would use.

The major use-case I've found for the fat-arrow in defining methods is when you want to use a method as a callback and that method references instance fields:

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"

As you see, you may run into problems passing a reference to an instance's method as a callback if you don't use the fat-arrow. This is because the fat-arrow binds the instance of the object to this whereas the thin-arrow doesn't, so thin-arrow methods called as callbacks as above can't access the instance's fields like @msg or call other instance methods. The last line there is a workaround for cases where the thin-arrow has been used.

一萌ing 2025-01-04 09:53:43

其他答案中未提及的一点需要注意的是,在不必要时将函数与粗箭头绑定可能会导致意想不到的结果,例如在本示例中,我们将调用一个名为 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. 当满足以下条件时,应使用直接调用粗箭头(未演示)函数的细箭头函数:

    • 粗箭头函数必须始终附加到实例
    • 但是细箭头函数可能会发生变化(甚至是不使用原始粗箭头函数的新函数)
    • 并且细箭头函数永远不需要通过引用传递

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

A point not mentioned in other answers that is important to note is that binding functions with fat arrow when it is not necessary can lead to unintended results such as in this example with a class we'll just call 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

In this case the functions do exactly what one might expect and there seems to be no loss to using fat arrow, but what happens when we modify the DummyClass prototype after it's already been defined (e.g. changing some alert or changing the output of a log):

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

As we can see overriding our previously defined function of the prototype causes some_function to be correctly overwritten but other_function remains the same on instances as fat arrow has caused other_function from the class to be bound to all instances so instances won't refer back to their class to find a 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

Even fat arrow won't work as fat arrow only causes the function to be bound to new instances (which do gain the new functions as one would expect).

However this leads to some problems, what if we need a function (e.g. in the case of switching a logging function to an output box or something) that will work on all existing instances (including event handlers) [as such we can't use fat arrows in original definition] but we still need access to internal attributes in an event handler [the exact reason we used fat arrows not thin arrows].

Well the simplest way to accomplish this is to merely include two functions in the original class definition, one defined with a thin arrow which does the operations that you wish to execute, and another defined with a fat arrow that does nothing but call the first function for example:

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

So when to use thin/fat arrows can be summed up fairly easy in four ways:

  1. Thin arrow alone functions should be used when the both conditions are mett:

    • The method will never be passed by reference including event_handlers e.g. you never have a case such as: some_reference = some_instance.some_method; some_reference()
    • AND the method should be universal over all instances so if the prototype function changes so does the method over all instances
  2. Fat arrow alone functions should be used when the following condition is met:

    • The method should be bound precisely to the instance at instance creation and remain permanently bound even if the function definition changes for the prototype, this includes all cases where the function should be an event handler and the event handler behaviour should be consistent
  3. Fat arrow function which directly calls a thin arrow function should be used when the following conditions are met:

    • The method is required to be called by reference such as an event handler
    • AND the functionality may change in future affecting existing instances by replacing the thin arrow function
  4. Thin arrow function which directly calls a fat arrow (not demonstrated) function should be used when the following conditions are met:

    • The fat arrow function must be always attached to the instance
    • BUT the thin arrow function may change (even to a new function which doesn't use the original fat arrow function)
    • AND the thin arrow function is never needed to be passed by reference

In all approaches it must be considered in the case where the prototype functions might be changed whether or not behaviour for specific instances will behave correctly for example although a function is defined with a fat arrow its behaviour may not be consistent within an instance if it calls a method that is changed within the prototype

潇烟暮雨 2025-01-04 09:53:43

通常, -> 就可以了。

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

在这两种情况下,使用粗箭头来声明该函数将允许它们工作。但除非你正在做一些奇怪的事情,否则通常不需要这样做。

因此,在您确实需要 => 之前,请使用 ->,并且默认情况下切勿使用 =>

Usually, -> is fine.

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

alert Foo.static() == Foo # true

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

Note how the static method return the class object for this and the instance returns the instance object for this.

What's happening is that the invocation syntax is providing the value of this. In this code:

foo.bar()

foo will be the context of the bar() function by default. So it just sorta works how you want. You only need the fat arrow when you call these function in some other way that does not use the dot syntax for invocation.

# 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()

In both of those cases, using a fat arrow to declare that function would allow those to work. But unless you are doing something odd, you usually don't need to.

So use -> until you really need => and never use => by default.

赤濁 2025-01-04 09:53:43

只是一个了解粗箭头不起作用的示例

:(@canvas undefined)

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

起作用:(@canvas Defined)

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

just an example for unstanding the fat arrow

not works: (@canvas undefined)

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

works: (@canvas defined)

class Test
  constructor: ->
    @canvas = document.createElement 'canvas'
    window.addEventListener 'resize', =>
      @canvas.width = window.innerWidth
      @canvas.height = window.innerHeight
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文