JavaScript の this って分かりにくいけど ES2015 で何か変わったの?
はじめに
JavaScript の勉強のために、開眼!JavaScript を読んだ。
開眼! JavaScript ―言語仕様から学ぶJavaScriptの本質
- 作者: Cody Lindley,和田祐一郎
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/06/19
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
JavaScriptの理解があやふやだった自分にとって、知らないことがたくさんあり勉強になることが多かった。
その中でも、this についての理解があやふやだったので、復習もかねて内容をまとめておこうと思う。
一般的な使い方
var User = function(name) { this.name = name; } var bob = new User('bob'); console.log(bob.name); // 出力: bob
この this は、生成されたインスタンス自身を指します。
このの使い方が、最も一般的でしっくりくる使い方だと思う。
入れ子関数内で使われる場合
var myNumber = { elements: [1, 2, 3], multiple: 3, multiply: function() { console.log(this.elements); // 出力: [1, 2, 3] var multipleElements = this.elements.map(function(element) { return element * this.multiple; }); console.log(multipleElements); // 出力: [NaN, NaN, NaN] } }; myNumber.multiply();
multiply は、elements の各要素に multiple の値を乗算した結果を出力するメソッドです。
実行すると、[3, 6, 9] が出力されることを期待しますが残念。
[NaN, NaN, NaN]と出力されます。
これ、私が最もよくやってしまう間違いでした。
最初の頃は、ほんと何が間違っているのかさっぱり分かりませんでした。
入れ子関数内の this はグローバルオブジェクトを参照
先ほどのコードの乗算処理中に this を出力してみます。
var multipleElements = this.elements.map(function(element) { console.log(this); // 出力: Window /_display/ return element * this.multiple; });
っと、このようにグローバルオブジェクトである window を参照してしまいます。
なんとも不思議です。
グローバルオブジェクトに multiple は未定義なので、NaN が返ってきていたということですね、はい。
解決方法
これもお決まりで、スコープチェーンを使って解決します。
親関数内に自身を参照できる変数を用意し、入れ子関数からはその変数を経由して参照します。
以下が修正版です。
var myNumber = { elements: [1, 2, 3], multiple: 3, multiply: function() { console.log(this.elements); // 出力: [1, 2, 3] var self = this; // myNumber への参照をセット var multipleElements = this.elements.map(function(element) { return element * self.multiple; }); console.log(multipleElements); // 出力: [3, 6, 9] } }; myNumber.multiply();
ローカル変数 self を用意し、そこに myNumber への参照をセットします。
入れ子関数から multiple の値を取得する際、この self を参照して取得します。
ちなみにこの仕様は、ECMAScript 5 で解決するということが記載されていました。
ES2015(ES6) の Arrow functions
最近、React の勉強がてら ES2015 でコードを意識して書いています。
その中で、上記の this が解決されているのか確認してみました。
結論を先に書くと、Arrow function 記法で書けば解決です!
Arrow functions 記法は、
list.map(function(i) { return i * i; });
を、以下のように書くことができます。
list.map((i) => i * i);
この Arrow functions 書き方も簡略化されていて見やすいですね。
その上、this の参照先がグローバルオブジェクトにならないので便利です。
ということで、最初のコードはスコープチェーンを使わなくても良くなります。
var myNumber = { elements: [1, 2, 3], multiple: 3, multiply: function() { console.log(this.elements); // 出力: [1, 2, 3] var multipleElements = this.elements.map((element) => element * this.multiple); console.log(multipleElements); // 出力: [3, 6, 9] } }; myNumber.multiply();
Arrow functions については、以下のページに色々説明があります。
英語が分からなくても、コードが読めれば大丈夫w
まとめ
ということで、this の内容のまとめでした。
Arrow functions で解決されたのが分かった時には「おぉ」ってなりましたw
今後は、こっちを使っていきます!
ES3 とか使わないといけないプロジェクトにアサインされた場合は、気をつけよう、うん。
開眼! JavaScript ―言語仕様から学ぶJavaScriptの本質
- 作者: Cody Lindley,和田祐一郎
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/06/19
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る