[JavaScript General] JavaScript における this と Arrow Function
this が何を指すか問題の整理。
Contents
定義
this キーワードはコンテキストオブジェクト (カレントオブジェクト) を参照します。一般的に、メソッド内では this は呼び出し元オブジェクト (calling object)を参照します。
「JavaScript逆引きレシピ jQuery対応」P.73 より引用、追記。
翔泳社
売り上げランキング: 210,969
文脈 | this の指すもの |
---|---|
Function | グローバルオブジェクト。Strict mode では undefined 。 |
call / apply | 引数で指定したオブジェクト。 |
EventListener | イベントの発生元。 |
Constructor | 生成するインスタンス。 |
Method | 呼び出し元のオブジェクト(= Receiver)。 |
Arrow function | アロー関数を定義したコンテキストでの this を捕捉。 |
実はJavaScriptにおいてthisは非常に簡単なルールで決定されます。そのルールとは,「呼び出した関数の手前(ドットの前)のオブジェクトがthisになる。ただし,手前にドットがない場合はグローバルオブジェクトがthisになる」というものです。
Function
グローバルオブジェクト。
Strict mode では undefined
。
'use strict';
function f() {
console.log(this);
return this;
}
f(); // undefined
関数呼び出し。
call / apply
引数で指定したオブジェクト。
'use strict';
function f() {
console.log(this);
return this;
}
f(); // undefined
f.call({name: 'Paul'}); // Object {name: "Paul"}
- applyとcallの使い方を丁寧に説明してみる – あと味
- 第16回 JavaScriptのthisとcall:これでできる! クロスブラウザJavaScript入門|gihyo.jp … 技術評論社
callとapplyの違いは引数を個別に指定するか,配列でまとめて指定できるかの違いです。
EventListener
イベントの発生元。
'use strict';
class MyEventLister {
constructor(name) {
this._name = name;
}
onMouseClick() {
console.log(this._name);
console.log(this);
}
}
const paul = new MyEventLister('Paul');
paul.onMouseClick(); // Paul, MyEventLister {_name: "Paul"}
console.log('----');
const el = document.getElementById('button');
el.addEventListener('click', paul.onMouseClick, false); // undefined, <div id="button"></div>
See the Pen JavaScript The value of this within the handler by DriftwoodJP (@DriftwoodJP) on CodePen.
Constructor
生成するインスタンス。
class Member {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
console.log(this); // Member {firstName: "小穂", lastName: "田畑"}
}
getName() {
return this.lastName + ' ' + this.firstName + '<br />';
}
}
var mem = new Member('小穂', '田畑');
document.writeln(mem.getName());
new
で生成したインスタンス自身 mem
になる。
Method
呼び出し元のオブジェクト(= Receiver)。
class Member {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
getName() {
console.log(this); // Member {firstName: "小穂", lastName: "田畑"}
return this.lastName + ' ' + this.firstName + '<br />';
}
}
var mem = new Member('小穂', '田畑');
document.writeln(mem.getName());
mem.getName()
の呼び出し元のオブジェクト mem
になる。
メソッド内での関数呼び出し
メソッド内で関数呼び出しを行うと、前述の通り this
は undefined
となる。
このため、クロージャを使って外側で this
を _this
, self
, that
といった変数に保存したり、bind
メソッドを利用していた。
詳しくは、後述の「Arrow Function」を参照のこと。
Arrow Function
アロー関数を定義したコンテキストでの this
を捕捉する機能がある。
JavaScriptではイベント駆動の処理をよく書きます。
例えばDOMがクリックされたら何か処理する、XHRのリクエストが完了したら何か処理をする場合などです。
このような処理をJavaScriptで実装するには、コールバック関数やイベントリスナと呼ばれるものを対象のオブジェクト(DOMやXHR)に設定します。
このコールバック関数を登録する時点でのthisにコールバック関数内からアクセスしたくなる場面がよくありますが、これまではクロージャを使ってthisを保存しておいたり、Function.prototype.bindを使ってthisを束縛したりしていました。
ES6ではArrow Functionと呼ばれる新たな関数定義|式が導入され、このthisに対する煩わしさを解消しています。
ES5 まで。
- 外側で this を self などの変数に保存しておく。
bind
メソッドによってthis
を束縛しておく。
// ES5: self, _this
var jhon = {
name: 'jhon',
helloLater: function() {
var _this = this;
// console.log(this); // Object {name: "jhon"}
setTimeout(function() {
// console.log(this); // window
console.log("Hello, I'm " + _this.name);
}, 1000);
}
}
jhon.helloLater();
// ES5: bind()
var ringo = {
name: 'ringo',
helloLater: function() {
// console.log(this); // Object {name: "ringo"}
setTimeout(function() {
// console.log(this); // Object {name: "ringo"}
console.log("Hello, I'm " + this.name);
}.bind(this), 1000);
}
}
ringo.helloLater();
ES2015から。
// ES2015: Arrow function
var george = {
name: 'george',
helloLater() {
setTimeout(() => {
// console.log(this); // object
console.log("Hello, I'm " + this.name);
}, 1000);
}
}
george.helloLater();
See the Pen Javascript ES2015 Arrow function vs. _this vs. bind() by DriftwoodJP (@DriftwoodJP) on CodePen.
補遺
こちらで紹介されていた参考リンクを読む。
以下、リンクされていた参考サイトより引用。
このように、JavaScriptにおけるメソッドとは、特定のオブジェクトと密に結びついているものではありません。そのため、プログラムの文脈によっては、thisが指すオブジェクトも全く異なってくることを覚えておいてください。(略)
オブジェクト指向的なJavaScriptプログラミングを行う際には、今回お見せしたように、thisが指す対象を間違ってしまうことが多いので注意してください。
ここで気づくのは、(関数である)オブジェクト f は、他のオブジェクト a の内部変数をいじる!ってことです。オブジェクト指向はカプセル化だよね〜とか思っている私のように古いタイプ(?)の人間は、this.x と書かれたらまさか他のオブジェクトの内部変数をいじってるとは思わないので、これを理解するというか納得するのに時間がかかりました。(いや、分かってしまえば、「関数がメソッドになる」「関数は独立したオブジェクトだ」とはそういう意味なんだけれども…)
というわけで私と同類な人向けに:JavaScript の this はカプセル化じゃありませんよ!
提示していただいた例と併せ、サイ本の「変数のスコープの検討」という節を読んで、ようやくthisキーワードがスコープチェーンをたどって変数名を解決するというメカニズムが理解できました。
さらに追記。