コールバック内で正しい `this` にアクセスする方法 質問する

コールバック内で正しい `this` にアクセスする方法 質問する

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

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);
};

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

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

ベストアンサー1

知っておくべきこと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`

についてさらに詳しく知りたい場合はthisMDNドキュメント


正しい参照方法this

使用矢印関数

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

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

使用しないでくださいthis

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

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

は通常の変数なので、レキシカルスコープのルールに従い、コールバック内でアクセスできます。また、コールバック自体の値にselfアクセスできるという利点もあります。this

thisコールバックの明示的な設定- パート 1

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

すべての関数にはメソッドがある.bind [ドキュメント]は、値にバインドされた新しい関数を返しますthis。この関数は、呼び出した関数とまったく同じ動作をしますが.bindthis設定はユーザーによって行われます。その関数が呼び出される方法やタイミングに関係なく、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ますMyConstructorthis

注: jQueryのバインディングコンテキストを使用する場合は、jQuery.proxy [ドキュメント]代わりに、これを行う理由は、イベント コールバックをアンバインドするときに関数への参照を保存する必要がないようにするためです。jQuery はこれを内部的に処理します。

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

コールバックを受け入れる関数/メソッドの中には、コールバックが参照する値も受け入れるものがありますthis。これは基本的に自分でバインドするのと同じですが、関数/メソッドが代わりに実行します。Array#map [ドキュメント]はそのようなメソッドです。そのシグネチャは次のとおりです。

array.map(callback[, thisArg])

最初の引数はコールバックで、2 番目の引数は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 関連コールバックのコンテキストになります。


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

この問題のもう 1 つの一般的な兆候は、オブジェクト メソッドがコールバック/イベント ハンドラーとして使用される場合です。関数は JavaScript の第一級オブジェクトであり、「メソッド」という用語は、オブジェクト プロパティの値である関数の口語的な用語にすぎません。ただし、その関数には、その「包含」オブジェクトへの特定のリンクがありません。

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

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

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

関数はthis.methodクリック イベント ハンドラとして割り当てられていますが、 がdocument.bodyクリックされた場合、ログに記録される値は になります。undefinedこれは、イベント ハンドラ内ではが のインスタンスではなくthisを参照するためです冒頭で述べたように、を参照するものは、関数がどのように定義されているかではなく、関数がどのように呼び出されるかによって異なります。コードが次のようであれば、関数にオブジェクトへの暗黙的な参照がないことがより明らかになります。document.bodyFoo
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();

おすすめ記事