ecmascript:class
文書の過去の版を表示しています。
class
クラス構文
class Test { // 静的フィールド static version = '1.0.0'; // 静的メソッド static info() { // 注意 staticコンテキストでのthis.nameはクラス名を指す(`#name`ではない) return `${this,name} class, version ${this.version}` } // privateフィールド #name = ''; // コンストラクタ constructor(v) { this.#name = v; } // メソッド getMessage() { return 'Hello from ${this.#name}' } // getter get name() { return this.#name; } // setter set name(v) { this.#name = v; } }
クラスの実態は関数。(JSのクラスはオブジェクト生成構文のシンタックスシュガーに過ぎない)
console.log(typeof Test); // => function
継承
class Test2 extends Test { // 拡張メソッド print() { console.log(this.getMessage()); } }
関数とnew
ここからはマニア向けのディープな話になる。JSの関数はコンストラクタとして機能し、new演算子によって新しいオブジェクトを生成できる
function Fuga(x) { this.x = x; } const fuga = new Fuga(1); console.log(typeof Fuga); // => function // 普通の関数としても呼べる Fuga();
以下のコードは実は上記のコードとほぼ同等。class構文で定義したクラスは実は関数なのである。このような関数はコンストラクタ関数と呼ばれる
class Fuga { constructor(x) { this.x = x; } } const fuga = new Fuga(1); console.log(typeof Fuga); // => function // ただしclassで定義した関数は直接呼べないように制限がかかっている Fuga(); // TypeError: Class constructor Fuga cannot be invoked without 'new'
※ ただしfunctionによるコンストラクタ定義は過去のコードとの互換性のために残されているので現在は非推奨でありclassキーワードを使うべき
これは何を意味しているかというとclassと銘打っているが、その実体はクラスではないということだ。クラスのような振る舞いはprototype chainという仕組みで行われている。
constructorプロパティ
生成されたオブジェクトはconstructorというプロパティを持っている。これは自身を生成したコンストラクタ関数への参照である。
class Hoge { constructor(name) { this.name = name; } getName() { return this.name; } } const h = new Hoge(1) console.log(h,constructor === Hoge); // => true const h2 = new h.constructor(2); // newすることもできる
prototypeプロパティ
コンストラクタ関数はprototypeというプロパティを持っている
console.log(Hoge.prototype);
classで定義したメソッドは、このprototypeプロパティが持っている。
console.log(Hoge.prototype,getName); // => [function getName] console.log(Hoge.prototype,getName.call({ name: "にゃん" })); // => にゃん
コンストラクタ関数を通して生成されたオブジェクトは__proto__
というプロパティを持っており、コンストラクタ関数のprototypeにアクセスできる
console.log(h.__proto__.getName); // => [function getName]
h.__proto__
も__proto__
を持っている。このようなprototypeの連鎖をprototype chainと呼ぶ
console.log(h.__proto__); // => {} console.log(h.__proto__.__proto__); // => [Object: null prototype] {}
この場合は2つまでだが、クラスを継承することでprototype chainは長くなっていく。これはメソッドの呼び出しに関係している
x.m()というコードを実行すると
x
自身プロパティ一覧からmという名前のプロパティを探し、見つかったらメソッドとして実行する。なければ2へx,__proto__
のプロパティ一覧からmという名前の・・・、なければ3へx.__proto__.__proto__
のプロパティ一覧からmという名前の・・・、なければ4へ- prototype chainの末端まで到達しても見つからない場合はTypeErrorとなる
ecmascript/class.1757434975.txt.gz · 最終更新: by nullpon