web-dev-qa-db-ja.com

CoffeeScript、太い矢印(=>)を矢印(->)の上で使用する場合、およびその逆

CoffeeScriptでクラスを構築する場合、=>(「太い矢印」)演算子を使用してすべてのインスタンスメソッドを定義し、->演算子を使用して定義されるすべての静的メソッドを定義する必要がありますか?

129
Ali Salehi

いいえ、それは私が使用するルールではありません。

メソッドを定義する際の太い矢印の主な使用例は、メソッドをコールバックとして使用する場合で、そのメソッドはインスタンスフィールドを参照します。

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やcallのようなインスタンスのフィールドにアクセスできないためです他のインスタンスメソッド。最後の行には、細い矢印が使用されている場合の回避策があります。

152
nicolaskruchten

注意すべき重要な他の回答で言及されていない点は、必要でないときに太い矢印で関数をバインドすると、この例のように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

太い矢印でも機能は新しいインスタンスにバインドされるだけなので、太い矢印でも機能しません(期待どおりに新しい機能を取得します)。

しかし、これはいくつかの問題につながります。すべての既存のインスタンス(イベントハンドラーを含む)で機能する関数(たとえば、ログ機能を出力ボックスなどに切り替える場合)が必要な場合[使用できない元の定義では太い矢印]ですが、イベントハンドラの内部属性にアクセスする必要があります[細い矢印ではなく太い矢印を使用した正確な理由]。

これを実現する最も簡単な方法は、元のクラス定義に2つの関数を含めるだけです。1つは実行する操作を行う細い矢印で定義され、もう1つは最初の関数を呼び出すだけの太い矢印で定義されます例えば:

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

したがって、細い/太い矢印を使用する場合は、次の4つの方法で簡単にまとめることができます。

  1. 両方の条件が満たされている場合、細い矢印のみの機能を使用する必要があります。

    • このメソッドは、event_handlersなどの参照によって渡されることはありません。次のようなケースはありません。some_reference = some_instance.some_method; some_reference()
    • そして、メソッドはすべてのインスタンスで汎用的である必要があるため、プロトタイプ関数が変更されるとすべてのインスタンスでメソッドが変更されます
  2. 次の条件が満たされている場合は、太い矢印のみの機能を使用する必要があります。

    • メソッドはインスタンス作成時にインスタンスに正確にバインドされ、プロトタイプの関数定義が変更されても永続的にバインドされたままである必要があります。これには、関数がイベントハンドラーであり、イベントハンドラーの動作が一貫しているすべてのケースが含まれます
  3. 細い矢印関数を直接呼び出す太い矢印関数は、次の条件が満たされている場合に使用する必要があります。

    • メソッドは、イベントハンドラーなどの参照によって呼び出される必要があります。
    • また、細い矢印関数を置き換えることにより、既存のインスタンスに影響する機能が将来変更される可能性があります
  4. 次の条件が満たされている場合、太い矢印(デモンストレーションなし)関数を直接呼び出す細い矢印関数を使用する必要があります。

    • 太い矢印関数は常にインスタンスにアタッチする必要があります
    • ただし、細い矢印関数は変更される場合があります(元の太い矢印関数を使用しない新しい関数であっても)
    • そして、細い矢印関数は参照によって渡される必要はありません

すべてのアプローチで、たとえば特定のインスタンスの動作が正しく動作するかどうかに関係なくプロトタイプ関数が変更される可能性がある場合、関数が太い矢印で定義されている場合、その動作はインスタンス内で一貫性がない場合がありますプロトタイプ内で変更されるメソッド

13
Jamesernator

通常、_->_で問題ありません。

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

どちらの場合でも、太い矢印を使用してその関数を宣言すると、それらが機能します。しかし、奇妙なことをしているのでなければ、通常は必要ありません。

したがって、本当に_->_が必要になるまで_=>_を使用し、デフォルトでは_=>_を使用しないでください。

9
Alex Wayne

太い矢印を理解しないための単なる例

動作しません:(@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
5
user3896501