CoffeeScriptでクラスを作成する場合、すべてのインスタンスメソッドを=>
(「太い矢印」)演算子を使用して定義し、すべての静的メソッドを->
演算子を使用して定義する必要がありますか?
4 に答える
いいえ、それは私が使用するルールではありません。
メソッドを定義する際のファットアローの主なユースケースは、メソッドをコールバックとして使用し、そのメソッドがインスタンスフィールドを参照する場合です。
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を使用しないと、インスタンスのメソッドへの参照をコールバックとして渡す際に問題が発生する可能性があります。これは、fat-arrowがオブジェクトのインスタンスをバインドするのthis
に対し、thin-arrowはバインドしないため、上記のようにコールバックと呼ばれるthin-arrowメソッドは、@msg
他のインスタンスメソッドのようにインスタンスのフィールドにアクセスしたり呼び出したりすることができません。最後の行には、細い矢印が使用されている場合の回避策があります。
他の回答で言及されていない注意すべき重要な点は、必要のないときに太い矢印で関数をバインドすると、この例では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つの方法でかなり簡単にまとめることができます。
両方の条件が満たされている場合は、細い矢印のみの関数を使用する必要があります。
- このメソッドは、event_handlersを含む参照によって渡されることはありません。たとえば、次のようなケースはありません。some_reference = some_instance.some_method; some_reference()
- また、メソッドはすべてのインスタンスでユニバーサルである必要があるため、プロトタイプ関数が変更されると、すべてのインスタンスでメソッドが変更されます。
次の条件が満たされる場合は、太い矢印のみの関数を使用する必要があります。
- メソッドは、インスタンスの作成時にインスタンスに正確にバインドされ、プロトタイプの関数定義が変更された場合でも永続的にバインドされたままである必要があります。これには、関数がイベントハンドラーであり、イベントハンドラーの動作が一貫している必要があるすべての場合が含まれます。
細い矢印関数を直接呼び出す太い矢印関数は、次の条件が満たされている場合に使用する必要があります。
- このメソッドは、イベントハンドラーなどの参照によって呼び出される必要があります
- また、細い矢印機能を置き換えることにより、機能が将来変更され、既存のインスタンスに影響を与える可能性があります
次の条件が満たされる場合は、太い矢印(図示せず)関数を直接呼び出す細い矢印関数を使用する必要があります。
- ファットアロー関数は常にインスタンスにアタッチする必要があります
- ただし、細い矢印関数は変更される可能性があります(元の太い矢印関数を使用しない新しい関数でも)
- また、細い矢印関数を参照で渡す必要はありません。
すべてのアプローチで、プロトタイプ関数が変更される可能性がある場合、たとえば、関数が太い矢印で定義されていても、特定のインスタンスの動作が正しく動作するかどうかを考慮する必要があります。プロトタイプ内で変更されたメソッド
通常->
は大丈夫です。
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()
どちらの場合も、太い矢印を使用してその関数を宣言すると、それらが機能するようになります。しかし、何か奇妙なことをしているのでない限り、通常はそうする必要はありません。
したがって->
、本当に必要になるまで使用し、デフォルト=>
では使用しないでください。=>
太い矢を理解するための単なる例
動作しません:(@ 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