constructor
Baseline Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since March 2017.
constructor
メソッドは、クラスで作成されたオブジェクトインスタンスの生成と初期化を行うための特殊なメソッドです。
メモ:
このページでは constructor
の構文を紹介します。すべてのオブジェクトに存在する constructor
プロパティについては、 Object.prototype.constructor
を参照してください。
試してみましょう
構文
解説
コンストラクターを使用すると、インスタンス化されたオブジェクトに対して、他のメソッドを呼び出す前に行う必要のある独自の初期化を行うことができます。
class Person {
constructor(name) {
this.name = name;
}
introduce() {
console.log(`こんにちは、私は${this.name}です。`);
}
}
const otto = new Person("オットー");
otto.introduce(); // こんにちは、私はオットーです。
独自のコンストラクターを提供しなかった場合は、既定のコンストラクターが提供されます。クラスが基底クラスである場合、既定のコンストラクターは空です。
constructor() {}
クラスが派生クラスの場合、既定のコンストラクターが親コンストラクターを呼び出し、与えられた引数を渡します。
constructor(...args) {
super(...args);
}
それがこのようなコードを動作させることができます。
class ValidationError extends Error {
printCustomerMessage() {
return `Validation failed :-( (details: ${this.message})`;
}
}
try {
throw new ValidationError("Not a valid phone number");
} catch (error) {
if (error instanceof ValidationError) {
console.log(error.name); // これは ValidationError の代わりのエラー
console.log(error.printCustomerMessage());
} else {
console.log("Unknown error", error);
throw error;
}
}
ValidationError
クラスは、独自の初期化を行う必要がないため、明示的なコンストラクターは必要ありません。
既定のコンストラクターは、与えられた引数から親の Error
の初期化を行います。
ただし、独自のコンストラクターを提供し、クラスが親クラスから派生している場合は、 super()
を使用して親クラスのコンストラクターを明示的に呼び出す必要があります。
例えば、以下のようになります。
class ValidationError extends Error {
constructor(message) {
super(message); // 親クラスのコンストラクターの呼び出し
this.name = "ValidationError";
this.code = "42";
}
printCustomerMessage() {
return `検証に失敗しました :-( (details: ${this.message}, code: ${this.code})`;
}
}
try {
throw new ValidationError("正しい電話番号ではありません。");
} catch (error) {
if (error instanceof ValidationError) {
console.log(error.name); // これは ValidationError になる
console.log(error.printCustomerMessage());
} else {
console.log("未知のエラーです", error);
throw error;
}
}
クラスで new
を使用すると、以下の段階を踏みます。
- (派生クラスの場合)
super()
呼び出しが評価される前のconstructor
本体。この部分はまだ初期化されていないので、this
にアクセスしてはいけません。 - (派生クラスの場合)
super()
呼び出しが評価され、同じ処理で親クラスが初期化されます。 - 現在のクラスのフィールドが初期化されます。
super()
呼び出し後のconstructor
本体(基底クラスの場合は本体全体)が評価されます。
constructor
本体の中では、 this
で作成されるオブジェクトにアクセスしたり new
で呼び出されるクラスに new.target
でアクセスしたりすることができます。メソッド(ゲッター、セッターを含む)とプロトタイプチェーン は constructor
が実行される前に this
で初期化されているので、スーパークラスのコンストラクターからサブクラスのメソッドにアクセスすることもできることに注意してください。しかし、これらのメソッドが this
を使用している場合、 this
はまだ完全に初期化されていません。これは、派生クラスのパブリックフィールドを読むと undefined
になり、プライベートフィールドを読むと TypeError
になるということです。
new (class C extends class B {
constructor() {
console.log(this.foo());
}
} {
#a = 1;
foo() {
return this.#a; // TypeError: Cannot read private member #a from an object whose class did not declare it
// これは、クラスが宣言していないのではなく、スーパークラスの
// コンストラクターが実行されている時点で、プライベートフィールドが
// まだ初期化されていないため。
}
})();
constructor
メソッドは返値を持つことができます。基底クラスはコンストラクターから何らかの値を返すことができますが、派生クラスはオブジェクトまたは undefined
を返すか、 TypeError
を発生させなければなりません。
class ParentClass {
constructor() {
return 1;
}
}
console.log(new ParentClass()); // ParentClass {}
// 返値はオブジェクトではないので無視される。 これはコンストラクター関数と同じ。
class ChildClass extends ParentClass {
constructor() {
return 1;
}
}
console.log(new ChildClass()); // TypeError: Derived constructors may only return object or undefined
親クラスのコンストラクターがオブジェクトを返した場合、そのオブジェクトは派生クラスのクラスフィールドを定義する際の値として使用します。このトリックは「返値の上書き」と呼ばれ、派生クラスのフィールド(プライベートなものも含む)を無関係なオブジェクトに定義することができます。
constructor
は通常のメソッド構文に従うので、デフォルト引数や残余引数などをすべて使用することができます。
class Person {
constructor(name = "名無し") {
this.name = name;
}
introduce() {
console.log(`こんにちは、私は${this.name}`);
}
}
const person = new Person();
person.introduce(); // こんにちは、私は名無し
コンストラクターはリテラル名でなければなりません。計算されたプロパティ名はコンストラクターにはなれません。
class Foo {
// これは計算プロパティ名です。コンストラクターとしてピックアップされることはありません。
["constructor"]() {
console.log("called");
this.a = 1;
}
}
const foo = new Foo(); // ログ出力なし
console.log(foo); // Foo {}
foo.constructor(); // "called" と出力
console.log(foo); // Foo { a: 1 }
非同期メソッド、ジェネレータメソッド、アクセサ、クラスフィールドは constructor
と名付けることは禁止されています。プライベートな名前を #constructor
と呼び出すことはできません。 constructor
という名前のメンバーはプレーンなメソッドでなければなりません。
例
constructor メソッドの使用
このコードスニペットは、classes sample (ライブデモ) から転載しています。
class Square extends Polygon {
constructor(length) {
// ここでは、ポリゴンの幅と高さを指定された長さにして、親クラスの
// コンストラクターを呼び出しています。
super(length, length);
// メモ: 派生クラスでは、`this` を使用する前に `super()` を呼び出す
// 必要があります。これを省略すると ReferenceError が発生します。
this.name = "Square";
}
get area() {
return this.height * this.width;
}
set area(value) {
this.height = value ** 0.5;
this.width = value ** 0.5;
}
}
異なるプロトタイプにバインドされたコンストラクターでの super を呼び出し
super()
は現在のクラスのプロトタイプであるコンストラクターを呼び出します。現在のクラスのプロトタイプを変更した場合、 super()
は新しいプロトタイプのコンストラクターを呼び出します。現在のクラスの prototype
プロパティを変更しても、 super()
が呼び出すコンストラクターには影響しません。
class Polygon {
constructor() {
this.name = "Polygon";
}
}
class Rectangle {
constructor() {
this.name = "Rectangle";
}
}
class Square extends Polygon {
constructor() {
super();
}
}
// Polygon の代わりに(基本クラスである) Rectangle を継承するようにする
Object.setPrototypeOf(Square, Rectangle);
const newInstance = new Square();
// newInstance はまだ Polygon のインスタンスです。
// Square.prototype のプロトタイプを変更していないので、
// newInstance のプロトタイプチェーンは以下のままです。
// newInstance --> Square.prototype --> Polygon.prototype
console.log(newInstance instanceof Polygon); // true
console.log(newInstance instanceof Rectangle); // false
// ただし、 super() はコンストラクターとして Rectangle を呼び出すため、
// newInstance の name プロパティは Rectangle のロジックで初期化されます。
console.log(newInstance.name); // Rectangle
仕様書
Specification |
---|
ECMAScript Language Specification # sec-static-semantics-constructormethod |
ブラウザーの互換性
BCD tables only load in the browser