ユーザ用ツール

サイト用ツール


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;
  } 
}

継承

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] {}

何も継承していないクラスの場合prototype chainは2つで終わりだが、継承すると継承した回数だけprototype chainは長くなる。

prototype chainはメソッドの呼び出しに関係している。例えばx.m()というコードを実行すると次のようにメソッドが検索される

  1. x自身プロパティ一覧からmという名前のプロパティを探し、見つかったらメソッドとして実行する。なければ2へ
  2. x,__proto__のプロパティ一覧からmという名前の・・・、なければ3へ
  3. x.__proto__.__proto__のプロパティ一覧からmという名前の・・・、なければ4へ
  4. prototype chainの末端まで到達しても見つからない場合はTypeErrorとなる
ecmascript/class.txt · 最終更新: by nullpon