[解決済み] コールバック内で正しい `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);
};
が、同じ問題が発生します。
どうすれば正しいオブジェクトにアクセスできますか?
どのように解決するのですか?
知っておきたいこと
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
は特にそうですが
そのオブジェクトが参照する
. そのため、単純にそのオブジェクトを参照する新しい変数を作成することが簡単な解決策となります。この変数には任意の名前を付けることができますが、一般的なものは次のとおりです。
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
[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
というのも、イベントハンドラの内部で
this
は
document.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();
関連
-
JavaScriptの関数この指摘の問題を説明
-
vueにおけるfilterの適用シーンについて解説します。
-
[解決済み] 配列の結合時に未定義のプロパティ 'push' を読み込むことができない
-
[解決済み] 期待される代入または関数呼び出し: 未使用式なし ReactJS
-
[解決済み] 私のJavaScriptコードは "No 'Access-Control-Allow-Origin' header is present on requested resource "というエラーを受け取りますが、Postmanはそうならないのはなぜですか?
-
[解決済み] ページを再読み込みせずにURLを変更するにはどうすればよいですか?
-
[解決済み] JavaScriptで現在の日付を取得するには?
-
[解決済み] セレクタの子を取得する方法は?
-
[解決済み] JavaScriptの正規表現でマッチしたグループにアクセスするにはどうすればよいですか?
-
[解決済み] setTimeout()コールバックにパラメータを渡すにはどうすればよいですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
JSクロスドメインソリューション リアクト構成 リバースプロキシ
-
HTML+CSS+JavaScriptで簡単な三目並べゲームを作成する。
-
vue ディレクティブ v-html と v-text
-
vueのプロジェクトでモックを使用する方法を知っていますか?
-
Vueの一般的な組み込みディレクティブの説明
-
[解決済み】"フォームが接続されていないため、フォームの送信がキャンセルされました "というエラーの取得について
-
[解決済み] Web API エラー - このリクエストはブロックされました; コンテンツは HTTPS で提供されなければなりません
-
[解決済み] Angular 2 - setTimeout内で'this'を使用する [duplicate].
-
nullのプロパティinnerHTMLを読み取れません エラーメッセージ
-
[解決済み】イベントハンドラ内のReactインスタンス(this)にアクセスできない【重複あり