1. ホーム
  2. javascript

[解決済み] コールバック内で正しい `this` にアクセスする方法

2022-03-20 05:20:59

質問

イベントハンドラを登録するコンストラクタ関数があります。

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', function () {
        alert(this.data);
    });
}

// Mock transport object
var transport = {
    on: function(event, callback) {
        setTimeout(callback, 1000);
    }
};

// called as
var obj = new MyConstructor('foo', transport);

しかし、このままでは data プロパティを使用します。見た目は this は、作成されたオブジェクトではなく、別のオブジェクトを参照しています。

また、無名関数の代わりにオブジェクトメソッドを使うようにしました。

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', this.alert);
}

MyConstructor.prototype.alert = function() {
    alert(this.name);
};

が、同じ問題が発生します。

どうすれば正しいオブジェクトにアクセスできますか?

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

知っておきたいこと this

this (別名:コンテキスト")は各関数内の特別なキーワードで、その値は いかに 関数がどのように、いつ、どこで定義されたかではなく、関数が呼び出されたかです。他の変数のようにレキシカルスコープに影響されることはありません(アロー関数を除く、後述)。以下はその例です。

function foo() {
    console.log(this);
}

// normal function call
foo(); // `this` will refer to `window`

// as object method
var obj = {bar: foo};
obj.bar(); // `this` will refer to `obj`

// as constructor function
new foo(); // `this` will refer to an object that inherits from `foo.prototype`

について詳しく知るには this をご覧ください。 MDNドキュメント .


正しい参照方法 this

使用方法 矢印機能

ECMAScript 6 を導入 矢印関数 これはラムダ関数と考えることができる。これはラムダ関数と考えることができる。 this バインディングを使用します。その代わりに this は、通常の変数と同じようにスコープで検索されます。つまり .bind . 特殊な動作はこれだけではありません。詳しくはMDNドキュメントを参照してください。

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', () => alert(this.data));
}

を使用しないでください。 this

にアクセスしたくない。 this は特にそうですが そのオブジェクトが参照する . そのため、単純にそのオブジェクトを参照する新しい変数を作成することが簡単な解決策となります。この変数には任意の名前を付けることができますが、一般的なものは次のとおりです。 selfthat .

function MyConstructor(data, transport) {
    this.data = data;
    var self = this;
    transport.on('data', function() {
        alert(self.data);
    });
}

以降 self は通常の変数であるため、レキシカルスコープルールに従い、コールバック内部でアクセス可能です。また、これは this の値は、コールバックの値そのものです。

を明示的に設定する this コールバックの - パート1

の値を制御できないように見えるかもしれません。 this というのは、値が自動的に設定されるからですが、実はそうではありません。

すべての関数には .bind [docs】をご覧ください。] を持つ新しい関数を返します。 this を値にバインドしています。この関数は、あなたが呼び出した .bind のみで、その this は自分で設定したものです。その関数がいつどのように呼ばれようとも this は常に渡された値を参照します。

function MyConstructor(data, transport) {
    this.data = data;
    var boundFunction = (function() { // parenthesis are not necessary
        alert(this.data);             // but might improve readability
    }).bind(this); // <- here we are calling `.bind()` 
    transport.on('data', boundFunction);
}

この場合、コールバックの this の値に対して MyConstructor 's this .

jQueryのバインディングコンテキストを使用する場合は jQuery.proxy [docs】をご覧ください。] の代わりに このようにする理由は、イベントコールバックを解除するときに関数への参照を保存する必要がないためです。

セット this コールバックの - パート2

コールバックを受け付ける関数/メソッドの中には、コールバックの this を参照する必要があります。これは基本的に自分でバインドするのと同じですが、関数/メソッドがそれをやってくれます。 Array#map <上 [docs】をご覧ください。] がそのようなメソッドです。そのシグネチャは

array.map(callback[, thisArg])

第一引数にコールバック、第二引数に値 this を参照する必要があります。以下は工夫された例です。

var arr = [1, 2, 3];
var obj = {multiplier: 42};

var new_arr = arr.map(function(v) {
    return v * this.multiplier;
}, obj); // <- here we are passing `obj` as second argument

に値を渡せるかどうか。 this は、通常その関数/メソッドのドキュメントに記載されています。例えば jQueryの $.ajax メソッド [ドキュメント] というオプションが記述されています。 context :

このオブジェクトは、すべてのAjax関連のコールバックのコンテキストにされる。


よくある問題:オブジェクトのメソッドをコールバック/イベントハンドラとして使用する場合

この問題のもう一つの一般的な現れ方は、オブジェクト・メソッドがコールバック/イベント・ハンドラとして使用されている場合です。JavaScript では関数は一級市民であり、"method" という用語は、オブジェクトのプロパティの値である関数を表す口語的な用語に過ぎません。しかし、その関数は、その "containing" オブジェクトへの特定のリンクを持っていません。

次のような例を考えてみましょう。

function Foo() {
    this.data = 42,
    document.body.onclick = this.method;
}

Foo.prototype.method = function() {
    console.log(this.data);
};

機能 this.method はクリックイベントハンドラとして割り当てられていますが、もし document.body がクリックされると、ログに記録される値は undefined というのも、イベントハンドラの内部で thisdocument.body のインスタンスではなく Foo .
冒頭ですでに述べたように、何 this は、その関数がどのようなものであるかに依存します。 呼ばれる であり、どのように 定義 .
もし、以下のようなコードであれば、関数がオブジェクトへの暗黙の参照を持っていないことがもっと明白になるかもしれません。

function method() {
    console.log(this.data);
}


function Foo() {
    this.data = 42,
    document.body.onclick = this.method;
}

Foo.prototype.method = method;

解決策 は前述と同じです。利用可能な場合は .bind を明示的にバインドすることで this を特定の値に設定します。

document.body.onclick = this.method.bind(this);

または、コールバック/イベントハンドラとして無名関数を使用して、オブジェクトのメソッドとして明示的に関数を呼び出し、オブジェクトに代入します ( this ) を別の変数に代入する。

var self = this;
document.body.onclick = function() {
    self.method();
};

または矢印関数を使用します。

document.body.onclick = () => this.method();