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"
ご覧のとおり、ファット矢印を使用しないと、コールバックとしてインスタンスのメソッドへの参照を渡す際に問題が発生する可能性があります。これは、ファット矢印がオブジェクトのインスタンスをthis
にバインドするのに対し、細い矢印はバインドしないため、上記のコールバックとして呼び出される細い矢印メソッドは、@msg
や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
この場合、関数は期待どおりに機能し、太い矢印を使用しても損失はありませんが、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()
_
どちらの場合でも、太い矢印を使用してその関数を宣言すると、それらが機能します。しかし、奇妙なことをしているのでなければ、通常は必要ありません。
したがって、本当に_->
_が必要になるまで_=>
_を使用し、デフォルトでは_=>
_を使用しないでください。
太い矢印を理解しないための単なる例
動作しません:(@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