76

jQueryイベントへのイベントハンドラーコールバックとして機能するメソッドが定義されたクラスをTypeScriptで記述しようとしました。

class Editor {
    textarea: JQuery;

    constructor(public id: string) {
        this.textarea = $(id);
        this.textarea.focusin(onFocusIn);
    }

    onFocusIn(e: JQueryEventObject) {
        var height = this.textarea.css('height'); // <-- This is not good.
    }
}

onFocusIn イベント ハンドラー内で、TypeScript は「this」をクラスの「this」として認識します。ただし、jQuery は this 参照をオーバーライドし、イベントに関連付けられた DOM オブジェクトに設定します。

1 つの代替方法は、コンストラクター内でラムダをイベント ハンドラーとして定義することです。この場合、TypeScript は非表示の _this エイリアスを使用して一種のクロージャーを作成します。

class Editor {
    textarea: JQuery;

    constructor(public id: string) {
        this.textarea = $(id);
        this.textarea.focusin((e) => {
            var height = this.textarea.css('height'); // <-- This is good.
        });
    }
}

私の質問は、この jQuery の動作を克服するために、TypeScript を使用してメソッドベースのイベント ハンドラー内で this 参照にアクセスする別の方法はありますか?

4

12 に答える 12

102

thisアロー関数構文を使用する場合、スコープは保持されます。これはTypeScript For JavaScript Programmers() => { ... }からの例です。

var ScopeExample = { 
  text: "Text from outer function", 
  run: function() { 
    setTimeout( () => { 
      alert(this.text); 
    }, 1000); 
  } 
};

アロー関数の構文は「レキシカルスコープ」を保持しているため、this.text注意してください。Text from outer function

于 2012-10-06T14:25:27.987 に答える
22

したがって、前述のように、メソッドが常にそのthisポインターにバインドされることを保証するための TypeScript メカニズムはありません (これは単に jQuery の問題ではありません)。必要なのはthis、コールバックを呼び出す前にポインターを復元するメソッドのプロキシを生成することです。次に、コールバックをイベントに渡す前に、そのプロキシでラップする必要があります。jQuery には、このためのメカニズムが組み込まれていjQuery.proxy()ます。そのメソッドを使用した上記のコードの例を次に示します ($.proxy()呼び出しが追加されていることに注意してください)。

class Editor { 
    textarea: JQuery; 

    constructor(public id: string) { 
        this.textarea = $(id); 
        this.textarea.focusin($.proxy(onFocusIn, this)); 
    } 

    onFocusIn(e: JQueryEventObject) { 
        var height = this.textarea.css('height'); // <-- This is not good. 
    } 
} 

これは合理的な解決策ですが、個人的には、開発者がプロ​​キシ呼び出しを含めることを忘れていることが多いため、この問題に対する別の TypeScript ベースの解決策を考え出しました。以下のクラスを使用HasCallbacksする必要があるのは、クラスを派生させることだけです。HasCallbacksその後、接頭辞が付いたメソッド'cb_'には、thisポインターが永続的にバインドされます。thisほとんどの場合、別のポインターを使用してそのメソッドを呼び出すことはできません。どちらのメカニズムも機能するため、使いやすい方を選択してください。

class HasCallbacks {
    constructor() {
        var _this = this, _constructor = (<any>this).constructor;
        if (!_constructor.__cb__) {
            _constructor.__cb__ = {};
            for (var m in this) {
                var fn = this[m];
                if (typeof fn === 'function' && m.indexOf('cb_') == 0) {
                    _constructor.__cb__[m] = fn;                    
                }
            }
        }
        for (var m in _constructor.__cb__) {
            (function (m, fn) {
                _this[m] = function () {
                    return fn.apply(_this, Array.prototype.slice.call(arguments));                      
                };
            })(m, _constructor.__cb__[m]);
        }
    }
}

class Foo extends HasCallbacks  {
    private label = 'test';

    constructor() {
        super();

    }

    public cb_Bar() {
        alert(this.label);
    }
}

var x = new Foo();
x.cb_Bar.call({});
于 2012-10-06T05:28:51.400 に答える
21

他のいくつかの回答で説明されているように、矢印構文を使用して関数を定義すると、へthisの参照が常に囲んでいるクラスを参照します。

あなたの質問に答えるために、ここに 2 つの簡単な回避策があります。

矢印構文を使用してメソッドを参照する

constructor(public id: string) {
    this.textarea = $(id);
    this.textarea.focusin(e => this.onFocusIn(e));
}

矢印構文を使用してメソッドを定義する

onFocusIn = (e: JQueryEventObject) => {
    var height = this.textarea.css('height');
}
于 2013-12-23T04:27:01.790 に答える
6

コンストラクターでメンバー関数をそのインスタンスにバインドできます。

class Editor {
    textarea: JQuery;

    constructor(public id: string) {
        this.textarea = $(id);
        this.textarea.focusin(onFocusIn);
        this.onFocusIn = this.onFocusIn.bind(this); // <-- Bind to 'this' forever
    }

    onFocusIn(e: JQueryEventObject) {
        var height = this.textarea.css('height');   // <-- This is now fine
    }
}

または、ハンドラーを追加するときにバインドするだけです。

        this.textarea.focusin(onFocusIn.bind(this));
于 2014-05-20T14:47:32.533 に答える
4

これを試して

class Editor 
{

    textarea: JQuery;
    constructor(public id: string) {
        this.textarea = $(id);
        this.textarea.focusin((e)=> { this.onFocusIn(e); });
    }

    onFocusIn(e: JQueryEventObject) {
        var height = this.textarea.css('height'); // <-- This will work
    }

}
于 2013-05-24T01:47:56.510 に答える
2

js eval 関数を使用できます。var realThis = eval('this');

于 2014-09-20T12:13:18.113 に答える
2

TypeScript は、ファット アロー ラムダ構文 (既存の JS コードがないため、後方互換性の観点から許容される) で提供される再マッピングの利便性this以外に、(通常の JavaScript の手段を超えて) 「実際の」参照に戻るための追加の方法を提供しません。式thisを使用している可能性があり=>ます)。

CodePlex サイトに提案を投稿することもできますが、言語設計の観点からは、コンパイラが提供できる適切なキーワードは現存する JavaScript コードで既に使用されている可能性があるため、おそらくここでできることはあまりありません。

于 2012-10-06T03:35:17.010 に答える
2

私は同様の問題に直面しました。.each()多くの場合this、後のイベントのために別の値として保持するために使用できると思います。

JavaScript の方法:

$(':input').on('focus', function() {
  $(this).css('background-color', 'green');
}).on('blur', function() {
  $(this).css('background-color', 'transparent');
});

TypeScript の方法:

$(':input').each((i, input) => {
  var $input = $(input);
  $input.on('focus', () => {
    $input.css('background-color', 'green');
  }).on('blur', () => {
    $input.css('background-color', 'transparent');
  });
});

これが誰かに役立つことを願っています。

于 2014-06-16T08:17:29.820 に答える
0

上記のすべての回答よりもはるかに簡単な解決策があります。基本的に、「this」をクラス「this」に変換する「=>」コンストラクトを使用する代わりに、キーワード関数を使用して JavaScript にフォールバックします。

class Editor {
    textarea: JQuery;

    constructor(public id: string) {
        var self = this;                      // <-- This is save the reference
        this.textarea = $(id);
        this.textarea.focusin(function() {   // <-- using  javascript function semantics here
             self.onFocusIn(this);          //  <-- 'this' is as same as in javascript
        });
    }

    onFocusIn(jqueryObject : JQuery) {
        var height = jqueryObject.css('height'); 
    }
}
于 2015-05-11T18:30:42.367 に答える
0

への参照を別の変数に保存できthisます..selfおそらく、その方法で参照にアクセスできます。私は typescript を使用しませんが、これは過去にバニラ JavaScript で成功した方法です。

于 2012-10-06T03:30:40.597 に答える