0

サブクラスによってオーバーライドできるが、コールバックとしても使用できるメソッドを持つクラスを作成したいと考えています。いずれかの状況でのみ、目的の動作を取得できるようです。次に例を示します。

class Parent
    constructor: () ->
        @foo = "foo"

    fooNotAlwaysDefined: () ->
        console.log("In parent.fooNotAlwaysDefined, @foo:#{@foo}")

    childNotCalled: () =>
        console.log("In parent.childNotCalled, @foo:#{@foo}")

class Child extends Parent
    fooNotAlwaysDefined: () ->
        console.log("In child.fooNotAlwaysDefined, @foo:#{@foo}")

    childNotCalled: () ->
        console.log("In child.childNotCalled, @foo:#{@foo}")

c = new Child()
c.fooNotAlwaysDefined()
c.childNotCalled()
process.nextTick(c.fooNotAlwaysDefined)
process.nextTick(c.childNotCalled)

私が望むのは、子関数が呼び出され、@foo が両方の使用 (c. およびコールバックとして) のスコープ内にあることです。これが私が得る出力です:

child.fooNotAlwaysDefined では、@foo:foo

parent.childNotCalled では、@foo:foo

child.fooNotAlwaysDefined では、@foo:undefined

parent.childNotCalled では、@foo:foo

私が見つけた最善の回避策は、process.nextTick に与えられた匿名関数内に fooNotAlwaysDefined をラップできることですが、それは理想的とは言えません。

process.nextTick(() -> c.fooNotAlwaysDefined())

child.fooNotAlwaysDefined では、@foo:foo

私が望む動作を得るためにクラスを構造化する方法はありますか?

編集: 回答: 以下の非常に役立つコメントの要約は、childNotCalled で見られる動作がバグであるということです。この動作は 1.6.1 で発生しているため、改善されている可能性がありますが、この問題は解決されていません。

2 番目の編集: この問題は 1.6.2 で完全に解決されたようです。

4

2 に答える 2

2

これは、this正しくバインドされていないという典型的な問題です。すでに見つけたものに加えて、2 つの可能な解決策:

childNotCalled太い矢印でも定義できます。

うーん、ちょっとひどい。太い矢印付きメソッドは、「通常は機能する」ため、コードの匂いのようなものですが、怠け者になり、 の値について考えないようにすることができますthis。よりクリーンな方法:

process.nextTick child.fooNotAlwaysDefined.bind(child)

これは、無名関数をラップするのとほぼchild同じですが、より宣言的であり、変数を別のものに再割り当てした場合でも機能します。fooNotAlwaysDefinedthisバインドされた の新しいインスタンスを返すchildため、次のティックで実行されたときに正しいthis.

これは、太い矢印を使用してメソッドを宣言することと非常に似ていますが、より明示的で効率的で理解しやすくなります (そのコード行から、正しいことをしていることがわかるからです。メソッドの定義をチェックして、正しいことを確認してください)。

スーパークラスの太い矢印が太い矢印も使用しない限り、子メソッドの実装をオーバーライドするという事実は、それらを私にとって魅力のないものにするもう1つのことです(コンパイルされたコードをチェックして、これがなぜなのかを確認してください。それがそれを行う唯一の方法であり、これを回避する方法はありません (ファット アロー メソッドを禁止する以外は) が、期待するものではありません)。これは CoffeeScript の機能であり、なくてもよいと思います。

于 2013-03-09T02:21:15.893 に答える
0

これはまさに、Underscore のbindAll方法が設計されている状況です。メソッドのセットを にバインドしてthis、コールバックとして使用できるようにします。また、CoffeeScript のファット アロー構文では失敗するように見える継承とうまく機能します。

http://underscorejs.org/#bindAll

Underscore または LoDash を使用したくない場合は、次をParentのコンストラクターに追加して、自分で行うことができます。

for methodName in ['fooNotAlwaysDefined', 'childNotCalled']
    @[methodName] = do(method = @[methodName]) =>
        => method.apply @, arguments

Underscore のbindAll方法を使用すると、よりクリーンになります。

_.bindAll @, 'fooNotAlwaysDefined', 'childNotCalled'

または、古いブラウザーとの互換性について心配していない場合 (Node で実行しているようです)、関数には組み込みのbindメソッドがあると想定できます。

@fooNotAlwaysDefined = @fooNotAlwaysDefined.bind @
@childNotCalled = @childNotCalled.bind @
于 2013-03-09T02:49:03.997 に答える