ES6におけるクラス(class)の内部原理を深く理解する
- 992単語
- 5分
- 13 Sep, 2024
ES6では、JavaScriptのオブジェクト指向プログラミングスタイルをより簡潔で理解しやすくするクラス(class
)が導入されました。クラスは他のプログラミング言語における従来のオブジェクト指向モデルのように見えますが、その内部は依然としてJavaScriptの既存のプロトタイプ継承メカニズムに基づいています。この記事では、ES6クラスの内部原理を詳しく解説し、その動作メカニズムを理解する手助けをします。
1. クラスの本質はコンストラクタ
ES6で定義されたクラスは、実際にはコンストラクタです。クラスの本質を typeof
キーワードで確認できます:
1class Person {2 constructor(name) {3 this.name = name;4 }5}6
7console.log(typeof Person); // "function"
クラスの定義は最終的に関数にコンパイルされます。つまり、クラスは単なるコンストラクタの糖衣構文です。
2. コンストラクタの動作原理
クラス内の constructor
メソッドは、クラスインスタンスを初期化するためのコンストラクタです。インスタンスを作成する際、new
キーワードでこのコンストラクタが呼び出されます:
1class Person {2 constructor(name) {3 this.name = name;4 }5}6
7const p = new Person("Alice");8console.log(p.name); // "Alice"
これは、従来のES5の関数コンストラクタと非常に似ています:
1function Person(name) {2 this.name = name;3}4
5const p = new Person("Alice");6console.log(p.name); // "Alice"
3. プロトタイプ継承
クラス内のメソッドは、そのプロトタイプに定義されます。これはES5のプロトタイプ継承メカニズムと完全に一致します。クラスのメソッドを定義すると、そのメソッドはクラスのプロトタイプに追加されます:
1class Person {2 constructor(name) {3 this.name = name;4 }5
6 greet() {7 console.log(`Hello, ${this.name}!`);8 }9}10
11const p = new Person("Alice");12p.greet(); // "Hello, Alice!"
ES5では、同じ効果を以下のように実現します:
1function Person(name) {2 this.name = name;3}4
5Person.prototype.greet = function () {6 console.log(`Hello, ${this.name}!`);7};8
9const p = new Person("Alice");10p.greet(); // "Hello, Alice!"
クラスの内部継承メカニズムは、依然としてプロトタイプチェーンを介して実現されています。
4. クラスの継承メカニズム
ES6では extends
キーワードを使用してクラスの継承をサポートしていますが、これはJavaScriptの既存のプロトタイプ継承に依存しています。サブクラスが親クラスを継承する際、サブクラスのプロトタイプは親クラスのプロトタイプを指し、これによりメソッドの継承が実現されます:
1class Animal {2 constructor(name) {3 this.name = name;4 }5
6 speak() {7 console.log(`${this.name} makes a noise.`);8 }9}10
11class Dog extends Animal {12 speak() {13 console.log(`${this.name} barks.`);14 }15}16
17const d = new Dog("Rex");18d.speak(); // "Rex barks."
これはES5におけるプロトタイプチェーンを手動で実装することに相当します:
1function Animal(name) {2 this.name = name;3}4
5Animal.prototype.speak = function () {6 console.log(`${this.name} makes a noise.`);7};8
9function Dog(name) {10 Animal.call(this, name);11}12
13Dog.prototype = Object.create(Animal.prototype);14Dog.prototype.constructor = Dog;15
16Dog.prototype.speak = function () {17 console.log(`${this.name} barks.`);18};19
20const d = new Dog("Rex");21d.speak(); // "Rex barks."
5. super
を使用した親クラスメソッドの呼び出し
super
キーワードは、サブクラスから親クラスのコンストラクタやメソッドを呼び出すことができます。サブクラスのコンストラクタ内で super
は親クラスのコンストラクタを呼び出し、親クラスの属性やメソッドを継承します:
1class Animal {2 constructor(name) {3 this.name = name;4 }5}6
7class Dog extends Animal {8 constructor(name, breed) {9 super(name); // 親クラスのコンストラクタを呼び出す10 this.breed = breed;11 }12}13
14const d = new Dog("Rex", "Labrador");15console.log(d.name); // "Rex"16console.log(d.breed); // "Labrador"
ES5では、親クラスのコンストラクタを明示的に呼び出して、同様の効果を実現できます:
1function Animal(name) {2 this.name = name;3}4
5function Dog(name, breed) {6 Animal.call(this, name); // 親クラスのコンストラクタを呼び出す7 this.breed = breed;8}9
10const d = new Dog("Rex", "Labrador");11console.log(d.name); // "Rex"12console.log(d.breed); // "Labrador"
まとめ
ES6クラスの導入により、JavaScriptのオブジェクト指向プログラミングがより簡潔な構文で書けるようになりましたが、その内部は依然としてコンストラクタとプロトタイプチェーンメカニズムに依存しています。クラスは実際にはコンストラクタの糖衣構文であり、クラスの継承メカニズムもプロトタイプチェーンに基づいています。super
を使用することで、サブクラスが親クラスのコンストラクタやメソッドを呼び出し、継承関係を実現します。クラスの内部原理を理解することで、JavaScriptのオブジェクト指向特性をより効果的に活用できるようになります。