【JavaScript】プロトタイプ - オブジェクトの継承
JavaScriptのプロトタイプについて解説します。
検証環境
プロトタイプ
JavaScriptはオブジェクトが他のオブジェクトから機能等を継承する(引き継ぐ)ことが可能です。
継承元のオブジェクトをプロトタイプと呼び、オブジェクトは必ずプロトタイプを持ちます。
また、プロトタイプのプロトタイプというような何層にも渡って階層化することもできます。
プロトタイプの確認
プロトタイプはオブジェクトをコンソールに出力することで確認できます。
let person = {};
console.log(person);
{}
___ih_hl_start
[[Prototype]]: Object
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
...
省略...
___ih_hl_end
※ 出力結果は実行環境により異なる場合があります。
プロトタイプは[[Prototype]]: Object
の部分です。
オブジェクトはデフォルトではObject
というプロトタイプを持ちます。
また、Object
はいくつかのメソッドを持っていることが確認できます。
プロトタイプの取得
プロトタイプを取得するにはObjectクラスのgetPrototypeOfメソッドを使用します。
クラスが未学習の方はここでは記法のみ覚えていただければ問題ありません。
let person = {};
___ih_hl_start
console.log(Object.getPrototypeOf(person));
___ih_hl_end
{__defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, __lookupSetter__: ƒ, …}
[[Prototype]]: Object
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
...
省略...
『プロトタイプの確認』で出力した[[Prototype]]: Object
と同等の内容が得られたことが確認できます。
プロトタイプの設定
オブジェクトのプロトタイプを任意のオブジェクトに設定できます。
設定にはObjectクラスのcreateメソッドを使用します。
クラスが未学習の方はここでは記法のみ覚えていただければ問題ありません。
let person = {
name: "unknown",
info: function() {
console.log("Name : " + this.name);
}
};
___ih_hl_start
let human = Object.create(person);
___ih_hl_end
console.log(human);
{}
[[Prototype]]: Object
info: ƒ ()
name: "unknown"
[[Prototype]]: Object
変数human
は変数person
に記憶されたオブジェクトをプロトタイプとしたオブジェクトを記憶します。
出力結果のプロトタイプからname
プロパティやinfo
メソッドが確認できることから、変数person
のオブジェクトをプロトタイプに設定できたことが分かります。
プロパティ・メソッドの継承
オブジェクトはプロトタイプのプロパティやメソッドを継承(引き継ぎ)します。
そのため、オブジェクトからプロトタイプのプロパティやメソッドにアクセスすることが可能です。
let person = {
name: "unknown",
info: function() {
console.log("Name : " + this.name);
}
};
let human = Object.create(person);
console.log(human);
___ih_hl_start
human.info();
___ih_hl_end
{}
Name : unknown
通常、未定義のメソッドを呼び出すとエラーが発生します。
そのため、変数human
のオブジェクトからinfo
メソッドを呼び出すとエラーが発生しそうですが、継承したオブジェクトにinfo
メソッドがあるため正常に実行されます。
プロパティ・メソッドのアクセスでは最初にオブジェクト自身を探索し、未定義の場合はプロトタイプから探します。
もし、プロトタイプにも未定義の場合は、更にプロトタイプのプロトタイプへと階層を下げて探索します。
プロパティ・メソッドの重複
オブジェクトとプロトタイプのプロパティ名やメソッド名が重複する場合、上層の方が優先されます。
let person = {
name: "unknown",
info: function() {
console.log("Name : " + this.name);
}
};
let human = Object.create(person);
___ih_hl_start
human.name = "TANAKA";
___ih_hl_end
console.log(human);
human.info();
{name: 'TANAKA'}
name: "TANAKA"
[[Prototype]]: Object
info: ƒ ()
name: "unknown"
Name : TANAKA
9行目で変数human
のオブジェクトにname
プロパティの値を設定しています。
出力結果からinfo
メソッドは変数human
のname
プロパティの値を参照していることが分かります。
※ プロトタイプのname
プロパティの値は変わりません。
また、メソッドにおいても同様です。
let person = {
name: "unknown",
info: function() {
console.log("Name : " + this.name);
}
};
let human = Object.create(person);
human.name = "TANAKA";
___ih_hl_start
human.info = function() {
console.log("My name is " + this.name);
}
___ih_hl_end
console.log(human);
human.info();
{name: 'TANAKA', info: ƒ}
info: ƒ ()
name: "TANAKA"
[[Prototype]]: Object
info: ƒ ()
name: "unknown"
My name is TANAKA
同名のメソッドを定義しつつ、プロトタイプのメソッドを呼び出す場合はプロトタイプを取得して呼び出します。
let person = {
name: "unknown",
info: function() {
console.log("Name : " + this.name);
}
};
let human = Object.create(person);
human.name = "TANAKA";
human.info = function() {
___ih_hl_start
Object.getPrototypeOf(this).info();
___ih_hl_end
console.log("My name is " + this.name);
}
console.log(human);
human.info();
{name: 'TANAKA', info: ƒ}
info: ƒ ()
name: "TANAKA"
[[Prototype]]: Object
info: ƒ ()
name: "unknown"
Name : unknown
My name is TANAKA
11行目のObject.getPrototypeOf(this).info()
の部分がプロトタイプの取得とメソッドの呼び出しです。
※ Object.getPrototypeOf(this)
で取得したプロトタイプに対して、info
メソッドを呼び出しています。
このようにすることでプロトタイプのプロパティやメソッドにアクセスできますが、プロトタイプからメソッドを呼び出した場合、プロパティやメソッドはプロトタイプのモノを参照することに注意してください。
例えば、上記例では11行目のinfo
メソッドはプロトタイプのname
プロパティを参照するため、値が"unknown"
になっています。