Javascript と言えば prototype ベースのスクリプト言語で、クラスの実装は関数でコンストラクタに当たるものをインスタンス化して、prototypeにメソッドを追加していく形が伝統的なやり方でした。
const HogeClass = function() { "use strict"; this.moge = "moge"; }; HogeClass.prototype.sayMoge = function() { "use strict"; return this.moge; }; const hoge = new HogeClass(); window.console.log(hoge.sayMoge()); // moge
こんな感じ、new 演算子を使うと関数をコンストラクタとして使う事が出来ます。
window.console.log(hoge.constructor); /* ƒ () { "use strict"; this.moge = "moge"; } */
こう書いてやると分かりやすいですね、ちゃんと constructor と言うプロパティに格納されているのが確認できます。
まぁ正直コレでいいじゃん感が凄いんだけど、実装されてるなら使ってみたいじゃないですか?噂の class 構文とやらを。
class構文を書いてみよう
今回は前々から気になっていた class 構文を使ってみようと思う。
ECMAScript 2015 で導入された JavaScript クラスは、JavaScript にすでにあるプロトタイプベース継承の糖衣構文です。クラス構文は、新しいオブジェクト指向継承モデルを JavaScript に導入しているわけではありません。
MDN web docs – クラス
class 構文自体は割と前から実装されていたので「新しい技術」って訳じゃないのだけど、私はこれまでずっと関数でクラスを実装してきたから完全に初見です。
どちらが優れているとかパフォーマンス的な部分は全く知らんけど、面白そうだし触ってみようぜ!って事でひとつ。
先立って軽く MDN のドキュメントを読んだ限りでは、確かに見た目はいわゆるクラスの書き方って感じで好印象。
でも Javascript の this って他の言語と考え方が全然違うから、クラス内のメソッドに this って書く時は注意しなきゃ行けないんだろうなって。
this の挙動が見たい
とにかく最初に確認したいのは this の挙動、ひとまず「いわゆるクラス」を書く感じで実装してみます。
ドキュメント読めば答えが分かっちゃうから、初見でないとこの楽しさは味わえない。
class Hoge { // コンストラクタは constructor 関数を使う constructor() { this.hoge = "moge"; } // メソッドは function とか書かなくて良いらしい showThis() { window.console.log(this.hoge); // moge と表示されるはず } } const $hoge = new Hoge(); $hoge.showThis(); // moge window.console.log($hoge.hoge); // moge
うん、ここまでは大丈夫そうです(※ちゃんとモゲモゲしてる)
問題は次、Javascript の this は「呼出し元を参照する」と言う仕様なので、class 構文で実装した場合の挙動が見てみたいですよね!
十中八九、普通に Javascript すると思うけど。
呼び出すのがクラス外なら何でも良いので、簡単にマウスイベントから呼んでみようか。
class Hoge { constructor() { this.hoge = "moge"; } showThis() { window.console.log(this.hoge); // moge と表示されて欲しい } } const $hoge = new Hoge(); document.addEventListener("click", $hoge.showThis); // undefined
そうですよね(知ってた)
アロー関数を使って呼び出してみる
アロー関数自身は this を持っておらず、アロー関数内で this が参照される場合は「通常の変数検索ルールに従う」と言う仕様になっています。
だから、マウスイベントから呼ばれたとしても $hoge.showThis 内での this は、まず自身のスコープ内の this を探し、見つからないから1つ外側のスコープ、即ち Hoge クラス自身の this を参照しに行くに違いない!と言う予想。
私自身まだアロー関数に慣れていないからアレなんだけど、そう言う事なんじゃなかろうか。
早速試してみましょう。
class Hoge { constructor() { this.hoge = "moge"; } showThis() { window.console.log(this.hoge); // moge と表示されて欲しい } } const $hoge = new Hoge(); document.addEventListener("click", () => $hoge.showThis()); // moge
お、行けた行けた。
MDN 読めば分かるけど、この関数は this や arguments を束縛せずに使えるように実装されたものらしいので、多分こんな感じの使い方であっていると思われる。
this を束縛しない
アロー関数以前は、関数の呼び出し方法に応じて自身の this 値を定義していました
- コンストラクタでは新しいオブジェクト
- strict モードの関数呼び出しでは undefined
- 「オブジェクトのメソッド」として呼び出された関数ではそのときのオブジェクト
- など
これは、オブジェクト指向プログラミングをする上で煩わしいということが分かりました。
MDN web docs – アロー関数
おまけ
好奇心は満たせるなら満たすべきって事で試したんだけど、メソッドをアロー関数で定義するのは class 構文的に NG のようでしたw
class Hoge { constructor() { this.hoge = "moge"; } showThis = () => { // iOS の Safari だとここでシンタックスエラーが出て止まる window.console.log(this.hoge); // moge と表示されて欲しい }; } const $hoge = new Hoge(); document.addEventListener("click", $hoge.showThis); // PC 版の Chrome はエラーが出ずに moge
何故か PC 版の Chrome だと構文エラー出ずに完走しちゃうのが気になるけど。
ちなみにコンストラクタ内であれば、
class Hoge { constructor() { this.hoge = "moge"; this.showThis = () => { window.console.log(this.hoge); // moge と表示されて欲しい }; } } const $hoge = new Hoge(); document.addEventListener("click", $hoge.showThis); // moge
こう言う書き方で思った通りの動きになるんだけど…メソッドはメソッドで定義したいからこれは流石に気持ち悪いなって。
いや、しかしアロー関数エライねw
正直ただの省略表記を実現するだけの実装だと思っていたけど、むしろスコープ関係が本体だったと言う。
コメント