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


133

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


サンプルコードを投稿できますか?
サルノルド2012年

この回答も参照してください。stackoverflow.com/a
17431824/

回答:


157

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

メソッドを定義する際の太い矢印について私が見つけた主なユースケースは、メソッドをコールバックとして使用し、そのメソッドがインスタンスフィールドを参照する場合です。

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"

ご覧のとおり、ファットアローを使用しないと、インスタンスのメソッドへの参照をコールバックとして渡すときに問題が発生する可能性があります。これは、fat-arrowがオブジェクトのインスタンスをバインドするのthisに対して、thin-arrowはバインドしないため、上記のようにコールバックとして呼び出されたthin-arrowメソッドは、インスタンスのフィールドにアクセスし@msgたり、他のインスタンスメソッドを呼び出したりできないためです。最後の行は、細い矢印が使用された場合の回避策です。


2
this細い矢印から呼び出されるを使用する場合はどうしますか。また、太い矢印で取得するインスタンス変数も使用しますか?
Andrew Mao

「最後の行は、細い矢印が使用された場合の回避策です」と言ったように。
nicolaskruchten 2012

あなたは私の質問を誤解したと思います。コールバックのデフォルトのスコープが、this使用したい変数に設定されているとします。ただし、クラスメソッドもthis参照したいので、クラスも参照します。の割り当ては1つしか選択できないthisので、両方の変数を使用できる最善の方法は何ですか?
Andrew Mao

@AndrewMaoコメントで私に答えてもらうのではなく、おそらくこのサイトに完全な質問を投稿する必要があります:)
nicolaskruchten

大丈夫です、質問はそれほど重要ではありません。しかし、私はそれがコードの最後の行で参照していたものではないことを明確にしたかっただけです。
Andrew Mao

13

他の回答で言及されていない重要な点は、関数が太い矢印でバインドされている必要がない場合、この例のように、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. ファットアロー(デモンストレーションなし)関数を直接呼び出す細いアロー関数は、次の条件を満たす場合に使用する必要があります。

    • ファットアロー関数は常にインスタンスにアタッチする必要があります
    • しかし、細い矢印の関数は変更される可能性があります(元の太い矢印の関数を使用しない新しい関数にさえ)
    • そして、細い矢印関数は、参照によって渡される必要はありません

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


9

通常->は大丈夫です。

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

alert Foo.static() == Foo # true

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

静的メソッドがのクラスオブジェクトをthis返し、インスタンスがのインスタンスオブジェクトを返す方法に注意してくださいthis

何が起こっているのかというと、呼び出し構文がの値を提供しているということですthis。このコードでは:

foo.bar()

foobar()デフォルトでは関数のコンテキストになります。だから、それはちょっとあなたが望むように動作します。呼び出しにドット構文を使用しない他の方法でこれらの関数を呼び出す場合にのみ、太い矢印が必要です。

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

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

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


1
次の場合は失敗します:x = obj.instance; alert x() == obj # false!
nicolaskruchten 2012年

2
もちろんそれは可能ですが、それは「間違ったこと」に該当します。これで私の回答を編集して=>、クラスの静的/インスタンスメソッドでがいつ必要になるかを説明しました。
Alex Wayne、

Nitpick:// is not a CoffeeScript comment一方# is a CoffeeScript comment
nicolaskruchten 2012年

setTimeout foo.bar, 1000「間違っている」とはどういうことですか?脂肪矢印を使用する方が、IMHOを使用するよりもはるかに優れていsetTimeout (-> foo.bar()), 1000ます。
nicolaskruchten 2012年

1
@nicolaskruchten setTimeoutもちろん、その構文にはケースがあります。しかし、最初のコメントは多少人為的なものであり、正当なユースケースを明らかにするのではなく、それがどのように壊れるかを明らかにするだけです。=>特に、インスタンス化にバインドする必要のある新しい関数を作成するためのパフォーマンスコストがあるクラスインスタンスメソッドでは、正当な理由で必要でない限り、を使用しないでください。
Alex Wayne、

5

太った矢を理解するためのほんの一例

動作しません:(@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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.