1. ホーム
  2. jquery

TypeScriptで'this'のエイリアスはあるのか?

2023-10-26 20:41:26

質問

TypeScriptで、jQueryのイベントに対するイベントハンドラーコールバックとして動作するメソッドを定義したクラスを書こうとしています。

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オブジェクトに設定します。

この場合、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を使用してメソッドベースのイベントハンドラ内でこの参照にアクセスする別の方法はありますか?

どのように解決するのですか?

このように、TypeScriptにはメソッドを常にその this ポインタに常にバインドされていることを保証する TypeScript のメカニズムは存在しない(そしてこれは jQuery だけの問題ではない)。 必要なのは、メソッドのプロキシを生成し、そのプロキシが this ポインタを復元するプロキシを生成することです。それから、イベントに渡す前に、そのプロキシでコールバックをラップする必要があります。 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({});