プロトタイプ継承 | JavaScript
https://developer.mozilla.org/ja/docs/Web/JavaScript
まずはここを見る。
概要
JavaScript のプロトタイプ継承について prototype プロパティと constructor プロパティおよびコンストラクタ関数1を中心に理解します。
まとめ
- JavaScript の関数は
Functionコンストラクタ関数のインスタンスです。しかし new 演算子とともに使用されたときにインスタンスを生成するコンストラクタ関数にもなります- JavaScript は new 演算子とともに関数が呼び出されると(つまりコンストラクタ関数として呼び出されると)関数の this に戻り値であるインスタンスを設定します。詳細は後述する擬似コードを参照してください(
Function自身もコンストラクタ関数です)
- JavaScript は new 演算子とともに関数が呼び出されると(つまりコンストラクタ関数として呼び出されると)関数の this に戻り値であるインスタンスを設定します。詳細は後述する擬似コードを参照してください(
Functionコンストラクタ関数のインスタンス 1 はprototypeプロパティを持ちます- インスタンスはコンストラクタ関数の prototype へのリンク
\_\_proto\_\_を持ちます(現在は\_\_proto\_\_を直接参照することは非推奨でありObject.getPrototypeOf()を使います) - インスタンスは自身を生成したコンストラクタ関数にポイントされた
constructorプロパティを持ちます
コンストラクタ関数の疑似コード
function Foo(x, y) {
/*
* 返却するインスタン用オブジェクトを作成
*/
// this = new Object();
this.x = x;
this.y = y;
/*
* return がなくても this を返す
*/
// return this;
}
確認
console.log(Array.prototype === Object.getPrototypeOf(['a', 'b'])); // true
console.log(Object.prototype === Object.getPrototypeOf(Array.prototype)); // true
console.log(Array.constructor === Function); // true
console.log(Object.constructor === Function); // true
console.log(typeof Function.prototype); // function
console.log(typeof Object.prototype); // object
console.log(((x) => x).constructor === Function); // true
console.log(Function.prototype === Object.getPrototypeOf((x) => x)); // true
補足(修正点): 元のサンプルでは
(x:number) => xのように TypeScript の型注釈が JavaScript のコード中に混在しており、そのままでは構文エラーになります。型注釈を除去し、期待値のコメントも揃えました。
コンストラクタ関数
| 型 | コンストラクタ関数 | コンストラクタ関数ショートハンド |
|---|---|---|
| オブジェクト | Object |
{} |
| 配列 | Array |
[] |
| 関数 | Function |
なし |
インスタンスはコンストラクタ関数から作成
// 例
const a = ['a', 'b']; // Array のショートハンド
class Foo {};
const f = new Foo();
単純な関数?それともコンストラクタ関数?
関数が単純な関数なのかコンストラクタ関数なのかは形式ではなく new での呼び出しを想定しているかで決まります。
// 単純な関数
function sum(x, y) {
return x + y;
}
// 単純な関数として実行
sum(1, 2);
// 3
// コンストラクタ関数
function Sum(x, y) {
this.x = x;
this.y = y;
}
Sum.prototype.add = function() {
return this.x + this.y;
};
// new で生成
const instance = new Sum(1, 2);
instance.add();
// 3
補足(修正点): 元のサンプルは単純な関数とコンストラクタ関数の両方を同じ名前
Sumで宣言していました。関数宣言は巻き上げ(hoisting)されるため、後から書かれたSumの定義が先の定義を上書きしてしまい、Sum(1, 2); // 3は実際には意図通りに動作しません(コンストラクタ版のSumが呼ばれてしまい、戻り値は3になりません)。ここでは単純な関数をsum、コンストラクタ関数をSumと名前を分け、addメソッドの不要な引数も削除しました。
詳細
prototype プロパティ
prototype へのリンク( __proto__ プロパティ)
すべてのオブジェクトインスタンスは、インスタンスを生成したコンストラクタ関数のprototypeにリンクする秘密のプロパティ(__proto__)を持っています。この秘密のリンクを使ってインスタンスのコンストラクタ関数のprototypeプロパティを取得できます。
開眼!JavaScript 042
※ 「すべてのオブジェクトインスタンス」はコンストラクタ関数も含みます。
後述するとおりコンストラクタ関数( Function のインスタンスがコンストラクタ関数か単純な関数かは new を使った呼び出しを想定しているかによる)は __proto__ と prototpype の両方を持つ。
// []は配列のリテラルコンストラクタ関数
const a = ['a', 'b'];
// __proto__は非推奨なので、かわりにObject.getPrototypeOf()を使用
// https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/proto
Array.prototype == Object.getPrototypeOf(a); // true
// クラスもカスタムコンストラクタ関数の糖衣構文
class Foo {};
const foo = new Foo();
Foo.prototype === Object.getPrototypeOf(foo); // true
prototype プロパティ
8.3 prototypeプロパティはすべてのFunction()インスタンスに自動的に付与される
開眼!JavaScript p111
- ビルトインコンストラクタ関数(
ArrayやNumber)は、Functionコンストラクタ関数のインスタンス - カスタムなコンストラクタ関数
function Hoge() { /* コード */}は、Functionコンストラクタ関数のインスタンス - class はコンストラクタ関数の糖衣構文
- つまり↑よりすべての
コンストラクタ関数はprototypeプロパティを持つ
// カスタムコンストラクタ関数
function Hoge() {};
Hoge.constructor === Function; // true
typeof Hoge.prototype; // object
// クラス
class Foo {};
Foo.constructor === Function; // true
typeof Foo.prototype; // Object
そしてFunction()のインスタンスで自動的に付与されたprototypeプロパティの__proto__にはObject.prototypeが設定されている。
constructor プロパティ
コンストラクタを使って作成されたインスタンスは自身を生成したコンストラクタ関数にポイントされたconstructorプロパティを持つ。
ref. 開眼!JavaScript 28
// 配列インスタンス
['a', 'b'].constructor == Array; // true
Array.constructor === Function; // true
// クラス
class Foo {};
const foo = new Foo();
foo.constructor === Foo; // true
Foo.constructor === Function; // true
コンストラクタリテラルとコンストラクタ関数
| 型 | リテラル | コンストラクタ関数 |
|---|---|---|
| オブジェクト | {} |
Object |
| 数値 | 123 |
Number |
| 真偽値 | true or false | Boolean |
| 文字列 | 'foo' |
String |
| 配列 | [] |
Array |
| 関数 | なし | Function |
- 数値、真偽値、文字列を除いてコンストラクタリテラルで作成されようがコンストラクタ関数で作成されようが機能は変わらない
- 数値、真偽値、文字列はリテラルで作成された場合は、生成後にオブジェクトのように扱われる(メソッドを呼び出すなど)までプリミティブな値となる(開眼!JavaScript 15)