Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Web/JavaScript/Reference/Classes 以下を更新 #17592

Merged
merged 8 commits into from
Dec 28, 2023
180 changes: 145 additions & 35 deletions files/ja/web/javascript/reference/classes/constructor/index.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,35 @@
---
title: コンストラクター
title: constructor
slug: Web/JavaScript/Reference/Classes/constructor
l10n:
sourceCommit: 41cddfdaeed4a73fb8234c332150df8e54df31e9
---

{{jsSidebar("Classes")}}

`constructor` メソッドは、 {{jsxref("Statements/class", "class")}} で作成されたオブジェクトの生成と初期化のための特殊なメソッドです。
**`constructor`** メソッドは、[クラス](/ja/docs/Web/JavaScript/Reference/Classes)で作成されたオブジェクトインスタンスの生成と初期化を行うための特殊なメソッドです。

> **メモ:** このページでは `constructor` の構文を紹介します。すべてのオブジェクトに存在する `constructor` プロパティについては、 {{jsxref("Object.prototype.constructor")}} を参照してください。
mfuji09 marked this conversation as resolved.
Show resolved Hide resolved

{{EmbedInteractiveExample("pages/js/classes-constructor.html")}}

## 構文

```js-nolint
constructor() { /* … */ }
constructor(argument0) { /* … */ }
constructor(argument0, argument1) { /* … */ }
constructor(argument0, argument1, /* …, */ argumentN) { /* … */ }
```
constructor([arguments]) { ... }
```

追加の構文上の制約があります。

- `constructor` という名前のクラスメソッドは、[ゲッター](/ja/docs/Web/JavaScript/Reference/Functions/get)、[セッター](/ja/docs/Web/JavaScript/Reference/Functions/set)、[非同期](/ja/docs/Web/JavaScript/Reference/Statements/async_function)、[ジェネレーター](/ja/docs/Web/JavaScript/Reference/Statements/function*)メソッドになることはできません。
- 1 つのクラスが複数の `constructor` メソッドを持つことはできません。

## 解説

コンストラクターを使用すると、インスタンス化されたオブジェクトに対して、他のメソッドを呼び出す前に行う必要のある独自の初期化を提供することができます
コンストラクターを使用すると、インスタンス化されたオブジェクトに対して、他のメソッドを呼び出す前に行う必要のある独自の初期化を行うことができます

```js
class Person {
Expand All @@ -26,29 +38,31 @@ class Person {
}

introduce() {
console.log(`Hello, my name is ${this.name}`);
console.log(`こんにちは、私は${this.name}です。`);
}
}

const otto = new Person("Otto");
const otto = new Person("オットー");

otto.introduce();
otto.introduce(); // こんにちは、私はオットーです。
```

独自のコンストラクターを提供しなかった場合は、既定のコンストラクターが提供されます。クラスが基底クラスである場合、既定のコンストラクターは空です。

```js
```js-nolint
constructor() {}
```

クラスが派生クラスの場合、既定のコンストラクターが親コンストラクターを呼び出し、与えられた引数を渡します。

```js
```js-nolint
constructor(...args) {
super(...args);
}
```

> **メモ:** 上記のような明示的なコンストラクターと既定のコンストラクターの異なる点は、後者が実際には[配列イテレーター](/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/@@iterator)を呼び出して[引数のスプレッド](/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax)を行わないことです。

それがこのようなコードを動作させることができます。

```js
Expand All @@ -62,7 +76,7 @@ try {
throw new ValidationError("Not a valid phone number");
} catch (error) {
if (error instanceof ValidationError) {
console.log(error.name); // This is Error instead of ValidationError!
console.log(error.name); // これは ValidationError の代わりのエラー
console.log(error.printCustomerMessage());
} else {
console.log("Unknown error", error);
Expand All @@ -71,37 +85,120 @@ try {
}
```

`ValidationError` クラスは、独自の初期化を行う必要がないため、明示的なコンストラクターは必要ありません。既定のコンストラクターは、与えられた引数から親の `Error` の初期化を行います。
`ValidationError` クラスは、独自の初期化を行う必要がないため、明示的なコンストラクターは必要ありません。
既定のコンストラクターは、与えられた引数から親の `Error` の初期化を行います。

ただし、独自のコンストラクターを提供し、クラスが親クラスから派生している場合は、 `super` を使用して親クラスのコンストラクターを明示的に呼び出す必要があります。例えば、以下のようになります。
ただし、独自のコンストラクターを提供し、クラスが親クラスから派生している場合は、 [`super()`](/ja/docs/Web/JavaScript/Reference/Operators/super) を使用して親クラスのコンストラクターを明示的に呼び出す必要があります。
例えば、以下のようになります。

```js
class ValidationError extends Error {
constructor(message) {
super(message); // call parent class constructor
super(message); // 親クラスのコンストラクターの呼び出し
this.name = "ValidationError";
this.code = "42";
}

printCustomerMessage() {
return `Validation failed :-( (details: ${this.message}, code: ${this.code})`;
return `検証に失敗しました :-( (details: ${this.message}, code: ${this.code})`;
}
}

try {
throw new ValidationError("Not a valid phone number");
throw new ValidationError("正しい電話番号ではありません。");
} catch (error) {
if (error instanceof ValidationError) {
console.log(error.name); // Now this is ValidationError!
console.log(error.name); // これは ValidationError になる
console.log(error.printCustomerMessage());
} else {
console.log("Unknown error", error);
console.log("未知のエラーです", error);
throw error;
}
}
```

クラスには "`constructor`" という名前の特別なメソッドが 1 つだけ存在します。クラス内に複数の `constructor` メソッドが存在すると、 {{jsxref("SyntaxError")}} エラーが発生します。
クラスで [`new`](/ja/docs/Web/JavaScript/Reference/Operators/new) を使用すると、以下の段階を踏みます。

1. (派生クラスの場合) `super()` 呼び出しが評価される前の `constructor` 本体。この部分はまだ初期化されていないので、 `this` にアクセスしてはいけません。
2. (派生クラスの場合) `super()` 呼び出しが評価され、同じ処理で親クラスが初期化されます。
3. 現在のクラスの[フィールド](/ja/docs/Web/JavaScript/Reference/Classes/Public_class_fields)が初期化されます。
4. `super()` 呼び出し後の `constructor` 本体(基底クラスの場合は本体全体)が評価されます。

`constructor` 本体の中では、 [`this`](/ja/docs/Web/JavaScript/Reference/Operators/this) で作成されるオブジェクトにアクセスしたり [`new`](/ja/docs/Web/JavaScript/Reference/Operators/new) で呼び出されるクラスに [`new.target`](/ja/docs/Web/JavaScript/Reference/Operators/new) でアクセスしたりすることができます。メソッド([ゲッター](/ja/docs/Web/JavaScript/Reference/Functions/get)、[セッター](/ja/docs/Web/JavaScript/Reference/Functions/set)を含む)と[プロトタイプチェーン](/ja/docs/Web/JavaScript/Inheritance_and_the_prototype_chain) は `constructor` が実行される前に `this` で初期化されているので、スーパークラスのコンストラクターからサブクラスのメソッドにアクセスすることもできることに注意してください。しかし、これらのメソッドが `this` を使用している場合、 `this` はまだ完全に初期化されていません。これは、派生クラスのパブリックフィールドを読むと `undefined` になり、プライベートフィールドを読むと `TypeError` になるということです。

```js example-bad
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` を返すか、 {{jsxref("TypeError")}} を発生させなければなりません。

```js
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
```

親クラスのコンストラクターがオブジェクトを返した場合、そのオブジェクトは派生クラスの[クラスフィールド](/ja/docs/Web/JavaScript/Reference/Classes/Public_class_fields)を定義する際の値として使用します。このトリックは[「返値の上書き」](/ja/docs/Web/JavaScript/Reference/Classes/Private_properties#オーバーライドしたオブジェクトの返却)と呼ばれ、派生クラスのフィールド([プライベート](/ja/docs/Web/JavaScript/Reference/Classes/Private_properties)なものも含む)を無関係なオブジェクトに定義することができます。

`constructor` は通常の[メソッド](/ja/docs/Web/JavaScript/Reference/Functions/Method_definitions)構文に従うので、[デフォルト引数](/ja/docs/Web/JavaScript/Reference/Functions/Default_parameters)や[残余引数](/ja/docs/Web/JavaScript/Reference/Functions/rest_parameters)などをすべて使用することができます。

```js
class Person {
constructor(name = "名無し") {
this.name = name;
}
introduce() {
console.log(`こんにちは、私は${this.name}`);
}
}

const person = new Person();
person.introduce(); // こんにちは、私は名無し
```

コンストラクターはリテラル名でなければなりません。計算されたプロパティ名はコンストラクターにはなれません。

```js
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` という名前のメンバーはプレーンなメソッドでなければなりません。

## 例

Expand All @@ -112,11 +209,11 @@ try {
```js
class Square extends Polygon {
constructor(length) {
// Here, it calls the parent class' constructor with lengths
// provided for the Polygon's width and height
// ここでは、ポリゴンの幅と高さを指定された長さにして、親クラスの
// コンストラクターを呼び出しています。
super(length, length);
// NOTE: In derived classes, `super()` must be called before you
// can use `this`. Leaving this out will cause a ReferenceError.
// メモ: 派生クラスでは、`this` を使用する前に `super()` を呼び出す
// 必要があります。これを省略すると ReferenceError が発生します。
this.name = "Square";
}

Expand All @@ -131,9 +228,9 @@ class Square extends Polygon {
}
```

### 他の例
### 異なるプロトタイプにバインドされたコンストラクターでの super を呼び出し

ここでは、 `Square` クラスのプロトタイプが変更されていますが、新しいインスタンスが作成されたときには、その基底クラスである `Polygon` のコンストラクターが呼び出されます
`super()` は現在のクラスのプロトタイプであるコンストラクターを呼び出します。現在のクラスのプロトタイプを変更した場合、 `super()` は新しいプロトタイプのコンストラクターを呼び出します。現在のクラスの `prototype` プロパティを変更しても、 `super()` が呼び出すコンストラクターには影響しません

```js
class Polygon {
Expand All @@ -142,21 +239,33 @@ class Polygon {
}
}

class Rectangle {
constructor() {
this.name = "Rectangle";
}
}

class Square extends Polygon {
constructor() {
super();
}
}

class Rectangle {}
// Polygon の代わりに(基本クラスである) Rectangle を継承するようにする
Object.setPrototypeOf(Square, Rectangle);

Object.setPrototypeOf(Square.prototype, Rectangle.prototype);
const newInstance = new Square();

console.log(Object.getPrototypeOf(Square.prototype) === Polygon.prototype); //false
console.log(Object.getPrototypeOf(Square.prototype) === Rectangle.prototype); //true
// newInstance はまだ Polygon のインスタンスです。
// Square.prototype のプロトタイプを変更していないので、
// newInstance のプロトタイプチェーンは以下のままです。
// newInstance --> Square.prototype --> Polygon.prototype
console.log(newInstance instanceof Polygon); // true
console.log(newInstance instanceof Rectangle); // false

let newInstance = new Square();
console.log(newInstance.name); //Polygon
// ただし、 super() はコンストラクターとして Rectangle を呼び出すため、
// newInstance の name プロパティは Rectangle のロジックで初期化されます。
console.log(newInstance.name); // Rectangle
```

## 仕様書
Expand All @@ -169,8 +278,9 @@ console.log(newInstance.name); //Polygon

## 関連情報

- [クラスの使用](/ja/docs/Web/JavaScript/Guide/Using_classes)ガイド
- [クラス](/ja/docs/Web/JavaScript/Reference/Classes)
- [静的初期化ブロック](/ja/docs/Web/JavaScript/Reference/Classes/Static_initialization_blocks)
- {{jsxref("Statements/class", "class")}}
- {{jsxref("Operators/super", "super()")}}
- {{jsxref("Statements/class", "クラス宣言", "", "true")}}
- {{jsxref("Operators/class", "クラス式", "", "true")}}
- {{jsxref("Classes")}}
- [Object.prototype.constructor](/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor)
- {{jsxref("Object.prototype.constructor")}}
Loading