[JavaScript General] JavaScript における this と Arrow Function
this が何を指すか問題の整理。
[markdown]
## 定義
> * [this – JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/this)
>
> this キーワードはコンテキストオブジェクト (カレントオブジェクト) を参照します。一般的に、メソッド内では this は呼び出し元オブジェクト (calling object)を参照します。
「JavaScript逆引きレシピ jQuery対応」P.73 より引用、追記。
翔泳社
売り上げランキング: 210,969
| 文脈 | this の指すもの |
|:————-:|—————————————————|
| Function | グローバルオブジェクト。Strict mode では `undefined` 。 |
| call / apply | 引数で指定したオブジェクト。 |
| EventListener | イベントの発生元。 |
| Constructor | 生成するインスタンス。 |
| Method | 呼び出し元のオブジェクト(= Receiver)。 |
| Arrow function | アロー関数を定義したコンテキストでの `this` を捕捉。 |
> * [第16回 JavaScriptのthisとcall:これでできる! クロスブラウザJavaScript入門|gihyo.jp … 技術評論社](http://gihyo.jp/dev/serial/01/crossbrowser-javascript/0016)
>
> 実はJavaScriptにおいてthisは非常に簡単なルールで決定されます。そのルールとは,「呼び出した関数の手前(ドットの前)のオブジェクトがthisになる。ただし,手前にドットがない場合はグローバルオブジェクトがthisになる」というものです。
## Function
グローバルオブジェクト。
Strict mode では `undefined` 。
“`:javascript
‘use strict’;
function f() {
console.log(this);
return this;
}
f(); // undefined
“`
関数呼び出し。
## call / apply
引数で指定したオブジェクト。
> * [Function.prototype.call() – JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Function/call)
> * [Function.prototype.apply() – JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Function/apply)
“`:javascript
‘use strict’;
function f() {
console.log(this);
return this;
}
f(); // undefined
f.call({name: ‘Paul’}); // Object {name: “Paul”}
“`
> * [applyとcallの使い方を丁寧に説明してみる – あと味](http://taiju.hatenablog.com/entry/20100515/1273903873)
> * [第16回 JavaScriptのthisとcall:これでできる! クロスブラウザJavaScript入門|gihyo.jp … 技術評論社](http://gihyo.jp/dev/serial/01/crossbrowser-javascript/0016)
>
> callとapplyの違いは引数を個別に指定するか,配列でまとめて指定できるかの違いです。
## EventListener
イベントの発生元。
“`:javascript
‘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,
“`
See the Pen JavaScript The value of this within the handler by DriftwoodJP (@DriftwoodJP) on CodePen.
## Constructor
生成するインスタンス。
“`:javascript
class Member {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
console.log(this); // Member {firstName: “小穂”, lastName: “田畑”}
}
getName() {
return this.lastName + ‘ ‘ + this.firstName + ‘
‘;
}
}
var mem = new Member(‘小穂’, ‘田畑’);
document.writeln(mem.getName());
“`
`new` で生成したインスタンス自身 `mem` になる。
> * [JavaScript におけるオブジェクト指向の特徴と ES2015 のクラス定義への書き換え | deadwood](https://www.d-wood.com/blog/2016/07/25_8328.html#i-2)
## Method
呼び出し元のオブジェクト(= Receiver)。
“`:javascript
class Member {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
getName() {
console.log(this); // Member {firstName: “小穂”, lastName: “田畑”}
return this.lastName + ‘ ‘ + this.firstName + ‘
‘;
}
}
var mem = new Member(‘小穂’, ‘田畑’);
document.writeln(mem.getName());
“`
`mem.getName()` の呼び出し元のオブジェクト `mem` になる。
> * [JavaScript におけるオブジェクト指向の特徴と ES2015 のクラス定義への書き換え | deadwood](https://www.d-wood.com/blog/2016/07/25_8328.html#i-2)
### メソッド内での関数呼び出し
メソッド内で関数呼び出しを行うと、前述の通り `this` は `undefined` となる。
> * [JavaScriptの「this」は「4種類」?? – Qiita](http://qiita.com/takeharu/items/9935ce476a17d6258e27)
このため、クロージャを使って外側で `this` を `_this`, `self`, `that` といった変数に保存したり、`bind` メソッドを利用していた。
> * [クロージャ – JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Closures)
詳しくは、後述の「Arrow Function」を参照のこと。
## Arrow Function
アロー関数を定義したコンテキストでの `this` を捕捉する機能がある。
> * [ES6時代のJavaScript – クックパッド開発者ブログ](http://techlife.cookpad.com/entry/2015/02/02/094607)
>
> JavaScriptではイベント駆動の処理をよく書きます。
> 例えばDOMがクリックされたら何か処理する、XHRのリクエストが完了したら何か処理をする場合などです。
> このような処理をJavaScriptで実装するには、コールバック関数やイベントリスナと呼ばれるものを対象のオブジェクト(DOMやXHR)に設定します。
> このコールバック関数を登録する時点でのthisにコールバック関数内からアクセスしたくなる場面がよくありますが、これまではクロージャを使ってthisを保存しておいたり、Function.prototype.bindを使ってthisを束縛したりしていました。
> ES6ではArrow Functionと呼ばれる新たな関数定義|式が導入され、このthisに対する煩わしさを解消しています。
ES5 まで。
– 外側で this を self などの変数に保存しておく。
– `bind` メソッドによって `this` を束縛しておく。
> * [Function.prototype.bind() – JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Function/bind)
> * [JavaScript の超便利なメソッド bind で this を制御する – Foreignkey, Inc.](http://foreignkey.toyao.net/archives/763)
> * [JavaScriptでbind()を使って部分適用する | プログラミング | POSTD](http://postd.cc/partial-application-in-javascript-using-bind/)
“`:javascript
// 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から。
> * [アロー関数 – JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/arrow_functions)
“`:javascript
// 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.
> * [ES2015(ES6)新構文:アロー関数(Arrow function)|もっこりJavaScript|ANALOGIC(アナロジック)](http://analogic.jp/arrow-function/)
## 補遺
こちらで紹介されていた参考リンクを読む。
> [JavaScript の this を理解する – tacamy.blog](http://tacamy.hatenablog.com/entry/2013/01/06/224718)
以下、リンクされていた参考サイトより引用。
* [JavaScriptのthisキーワードをちゃんと理解する – builder by ZDNet Japan](http://builder.japan.zdnet.com/script/sp_javascript-kickstart-2007/20371112/)
> このように、JavaScriptにおけるメソッドとは、特定のオブジェクトと密に結びついているものではありません。そのため、プログラムの文脈によっては、thisが指すオブジェクトも全く異なってくることを覚えておいてください。(略)
>
> オブジェクト指向的なJavaScriptプログラミングを行う際には、今回お見せしたように、thisが指す対象を間違ってしまうことが多いので注意してください。
* [考える日常: JavaScript の this のキモチ](http://kazutomi.blogspot.jp/2012/03/javascript-this.html)
> ここで気づくのは、(関数である)オブジェクト f は、他のオブジェクト a の内部変数をいじる!ってことです。オブジェクト指向はカプセル化だよね〜とか思っている私のように古いタイプ(?)の人間は、this.x と書かれたらまさか他のオブジェクトの内部変数をいじってるとは思わないので、これを理解するというか納得するのに時間がかかりました。(いや、分かってしまえば、「関数がメソッドになる」「関数は独立したオブジェクトだ」とはそういう意味なんだけれども…)
>
> というわけで私と同類な人向けに:JavaScript の this はカプセル化じゃありませんよ!
* [JavaScript の this について – IT戦記](http://d.hatena.ne.jp/amachang/20070917/1190015123)
> 提示していただいた例と併せ、サイ本の「変数のスコープの検討」という節を読んで、ようやくthisキーワードがスコープチェーンをたどって変数名を解決するというメカニズムが理解できました。
さらに追記。
> * [関数とthis · JavaScriptの入門書 #jsprimer](https://asciidwango.github.io/js-primer/basic/function-this/)
> * [ECMAScript 2015以降のJavaScriptの`this`を理解する | Web Scratch](http://efcl.info/2018/01/04/what-is-this/)
[/markdown]