CoffeeScriptでクラスを構築する場合、すべてのインスタンスメソッドを=>
(「ファットアロー」)演算子を使用して定義し、すべての静的メソッドを->
演算子を使用して定義する必要がありますか?
CoffeeScriptでクラスを構築する場合、すべてのインスタンスメソッドを=>
(「ファットアロー」)演算子を使用して定義し、すべての静的メソッドを->
演算子を使用して定義する必要がありますか?
回答:
いいえ、それは私が使用するルールではありません。
メソッドを定義する際の太い矢印について私が見つけた主なユースケースは、メソッドをコールバックとして使用し、そのメソッドがインスタンスフィールドを参照する場合です。
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
たり、他のインスタンスメソッドを呼び出したりできないためです。最後の行は、細い矢印が使用された場合の回避策です。
this
細い矢印から呼び出されるを使用する場合はどうしますか。また、太い矢印で取得するインスタンス変数も使用しますか?
this
使用したい変数に設定されているとします。ただし、クラスメソッドもthis
参照したいので、クラスも参照します。の割り当ては1つしか選択できないthis
ので、両方の変数を使用できる最善の方法は何ですか?
他の回答で言及されていない重要な点は、関数が太い矢印でバインドされている必要がない場合、この例のように、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つの方法でかなり簡単に要約できます。
両方の条件が満たされた場合は、細い矢印のみの関数を使用する必要があります。
ファットアローのみの関数は、次の条件を満たす場合に使用する必要があります。
細い矢印関数を直接呼び出すファットアロー関数は、次の条件を満たす場合に使用する必要があります。
ファットアロー(デモンストレーションなし)関数を直接呼び出す細いアロー関数は、次の条件を満たす場合に使用する必要があります。
すべてのアプローチで、特定のインスタンスの動作が正しく動作するかどうかなど、プロトタイプ関数が変更される可能性がある場合を考慮する必要があります。たとえば、関数が太い矢印で定義されている場合、関数が呼び出された場合、その動作はインスタンス内で一貫しない場合があります。プロトタイプ内で変更されたメソッド
通常->
は大丈夫です。
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()
どちらの場合も、太い矢印を使用してその関数を宣言すると、機能するようになります。しかし、奇妙なことをしているのでない限り、通常はする必要はありません。
したがって、->
本当に必要=>
になるまで使用し=>
、デフォルトでは決して使用しないでください。
x = obj.instance; alert x() == obj # false!
=>
、クラスの静的/インスタンスメソッドでがいつ必要になるかを説明しました。
// is not a CoffeeScript comment
一方# is a CoffeeScript comment
。
setTimeout foo.bar, 1000
「間違っている」とはどういうことですか?脂肪矢印を使用する方が、IMHOを使用するよりもはるかに優れていsetTimeout (-> foo.bar()), 1000
ます。
setTimeout
もちろん、その構文にはケースがあります。しかし、最初のコメントは多少人為的なものであり、正当なユースケースを明らかにするのではなく、それがどのように壊れるかを明らかにするだけです。=>
特に、インスタンス化にバインドする必要のある新しい関数を作成するためのパフォーマンスコストがあるクラスインスタンスメソッドでは、正当な理由で必要でない限り、を使用しないでください。
太った矢を理解するためのほんの一例
動作しません:(@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