JavaScriptのクラス構文はプロトタイプベースのシンタックスシュガーである
まえがき
対象者
- JavaScriptにおいて、クラス構文についてしっかり学びたい方
- クラス構文を使用しているが、その背後にある原理を理解したい方
本ページの目的
- JavaScriptがプロトタイプベース言語であることやシンタックスシュガー(糖衣構文)についてザックリ理解
- クラス構文=プロトタイプベースのオブジェクト指向プログラミング であることの理解
前提知識
シンタックスシュガー(糖衣構文)
プログラミングをやっていると一度は聞いたことがあるかもしれませんが、なじみもない方がいると思うので説明します。
シンタックスシュガーとはより読みやすく書きやすい構文にするための簡略化のことを指します。
シュガーがついているので料理に例えます。
料理において砂糖を使って料理を甘くできますが、砂糖は料理に必須のものではありません。同様に、シンタックスシュガーもプログラミングに必須のものではなく、それらを使わなくても同じことができますが、使うことでコードがより読みやすく書きやすくなります。
JavaScriptがプロトタイプベース言語であること
こちらもおさえておいたほうがいい内容なので説明します。プロトタイプベース言語とは、オブジェクト指向プログラミングで使われるスタイルの1つです。
-
並列に並ぶスタイルとして、「クラスベース」や「アスペクト指向」が存在します。
-
JavaScriptでは以下のように既存のオブジェクトに
prototype
を使って、メソッドを生やすことができます。
以下ような書き方ができるものがプロトタイプベース言語の特徴です。function Person(name, age) { this.name = name; } Person.prototype.hello = function () { console.log("hello" + this.name); }; const bob = new Person("Bob", 18); bob.hello()
こちらのコードを開発者ツール上で実行すると
[[prototype]]
のなかにhelloの関数が入っていることがわかります。 -
また、上のキャプチャを見ると
[[prototype]]
の中に[[prototype]]
が存在しています。
このようにオブジェクトが多重階層的につながっていることをプロトタイプチェーンといい、階層が浅いものから順々にプロパティが呼ばれます。 -
Wikipediaには
プロトタイプとは、複製元になったオブジェクトを意味しており、複製先のオブジェクトから見てそう呼ばれる。プロトタイプは同時にそのオブジェクトの暗黙の委譲先になり、これはプロトタイプを複製が継承していることと同じになる。
プロトタイプベース言語を無理やり日本語に訳すと「複製元オブジェクトベース言語」みたいな感じでしょうか。
JavaScriptのクラス構文について
前提知識が長くなってしまいましたが、タイトル「JavaScriptのクラス構文はプロトタイプベースのシンタックスシュガーである」についての説明をします。
JavaScriptは前述したとおり、実際にはプロトタイプベースのオブジェクト指向プログラミング言語です。
ですが、より構造化されたコードを書くために、シンタックスシュガーとしてクラス構文を利用できます。
コードで確認
プロトタイプベース
まず、プロトタイプベースでPersonインスタンスを作成します。
function PersonProto(name, age) {
this.name = name;
this.age = age;
}
PersonProto.prototype.hello = function () {
console.log("hello " + this.name);
};
const bobProto = new PersonProto("bob", 20);
console.log(bobProto);
結果は以下の画面キャプチャの通りです。
このようにメソッドhello
は[[prototype]]
に格納されています。
クラス構文
つぎに、クラスベースでPersonインスタンスを作成します。
class PersonClass {
constructor(name, age) {
this.name = name;
this.age = age;
}
hello() {
console.log("hello " + this.name);
}
}
const bobClass = new PersonClass("bob", 20);
console.log(bobClass);
結果は以下キャプチャのようになっています。こちらもメソッドhello
は[[prototype]]
に格納されていることがわかります。
[[prototype]]
同士が同じものをであることを確認しました。true
が出力されますね。
この仕組みを知らないと発生するトラブルについて
このタイトルを理解していないと、以下の例のような事象が発生します。
例えば、次のように、「Person」という名前のクラスを定義して、あとからage
プロパティを追加するとします。
class Person {
constructor(name) {
this.name = name;
}
hello() {
console.log(
`Hello, my name is ${this.name} and I am ${this.age} years old.`
);
}
}
const john = new Person("John");
john.age = 30; // プロパティをあとから追加
john.hello(); // Hello, my name is John and I am 30 years old.
この書き方は、プロトタイプベースであるからこそ後からage
プロパティを追加できていますが、保守性が悪くなっています。
例えば、john.age = 30
の記述場所を間違えた場合がundefined
出力されます。
class Person {
constructor(name) {
this.name = name;
}
hello() {
console.log(
`Hello, my name is ${this.name} and I am ${this.age} years old.`
);
}
}
const john = new Person("John");
john.hello(); // Hello, my name is John and I am undefined years old.
john.age = 30;
このように、「JavaScriptのクラス構文はプロトタイプベースのシンタックスシュガーである」であることを考えるとJavaScriptのコーディングが捗るかもしれません。
Discussion