😍原型链の紹介#
原型オブジェクト#
簡単な説明をします
JS では
すべてがオブジェクトに基づいています
- 関数の
prototype
は原型オブジェクトを指します- 原型オブジェクトの
constructor
は関数を指します- 原型オブジェクトに定義されたプロパティとメソッドは、関連するすべてのインスタンスオブジェクトで共有および継承されます
prototype
は関数固有のものです__proto__
プロパティは、オブジェクト固有のものです
-
JS で関数を作成すると、
prototype
プロパティが生成され、このプロパティはオブジェクトを指します。このオブジェクトは、その関数の原型オブジェクトです。 -
この関数のインスタンスオブジェクトを宣言すると、このオブジェクトは
__proto__
プロパティを実装し、この__proto__
プロパティは、このインスタンスオブジェクト(ha)
の原型オブジェクト({constructor:f})
を指します。したがって、関数のprototype
とインスタンスオブジェクトの__proto__
を等しく比較すると、もちろんtrue
になります。
// Haという名前の関数を作成します
function Ha(){
this.name='哈喇'
}
// Haのprototypeを呼び出します
Ha.prototype //{constructor:f}
// インスタンスオブジェクトを宣言します
let ha = new Ha()
// 原型オブジェクトの比較
ha.__proto__ === Ha.prototype // true
// コンストラクタはha.constructorを直接呼び出すことができます
ha.__proto__.constructor === ha.constructor
原型チェーン#
原型チェーンは非常にシンプルです。以下のコードを見れば理解できます。
ha.__proto__.__proto__.__proto__ // null
// ここでnullになる理由を説明します
ha.__proto__はHaの原型オブジェクトを指します
ha.__proto__.__proto__はHaの原型オブジェクトObjectの原型オブジェクトを指します
ha.__proto__.__proto__.__proto__は、すべてがオブジェクトに基づいているので、Objectの上位の原型オブジェクトは存在しますか?もちろん存在しませんので、nullを返します
// コンストラクタの継承
function Haha(name){
this.name=name
Ha.call(this)
}
// Hahaクラスのインスタンスをまず宣言し、インスタンスのnameプロパティを呼び出すと、Haのコンストラクタのnameプロパティの値が表示されます。継承を使用しているため、現在のインスタンスのコンストラクタに存在しないプロパティは、上位のHaのコンストラクタのnameプロパティを検索し、存在する場合は表示されます。存在しない場合は、さらに上位のObjectのコンストラクタのnameプロパティを検索し、存在しない場合はnullになります
let haha = new Haha()
haha.name // 哈喇
// Hahaクラスのインスタンスを宣言し、引数を渡して、インスタンスのnameプロパティを呼び出すと、王老五が表示されます
let haha = new Haha('王老五')
haha.name // 王老五
コンストラクタ、インスタンス、および原型の関係#
コンストラクタはインスタンスを作成するための関数です。各インスタンスには原型があり、原型はコンストラクタの原型を指します
// haの原型はHa.prototypeです
// Ha.prototypeの原型はObject.prototypeです
function Ha(){} // コンストラクタ
let ha=new Ha() // インスタンス
Ha.prototype.constructor === Ha //true コンストラクタの原型のconstructorはコンストラクタ自体を指します
継承#
コンストラクタの継承#
call () を使用して Parent の this を Child のインスタンスに指定して継承を実現します。
利点
- 親クラスのコンストラクタに引数を渡すことができます。
- 同じ親クラスから継承している他の子クラスに影響を与えずに、各子クラスが独自の親クラスインスタンスを持つことができます。
欠点
- 親クラスのコンストラクタ内のプロパティとメソッドのみを継承し、原型オブジェクトの内容は継承できません。
- 再利用できません
function Parent(){
this.name='father'
}
Parent.prototype.sayHello = function () {
return this.name
}
function Children(){
Parent.call(this)
}
const c = new Children()
c.name // father
c.sayHello // c.sayHello is not a function
原型チェーンの継承#
原型オブジェクトを上書きします
利点
- 関数の再利用、子クラスは親クラスのプロパティとメソッドを使用できます。
- 子クラスは親クラスの原型オブジェクト上のプロパティとメソッドに直接アクセスできます。
欠点
- 親クラスのコンストラクタに引数を渡すことはできません。
- 親クラスの参照プロパティは、すべての子クラスに共有され、1 つの子クラスが参照プロパティを変更すると、他の子クラスも影響を受けます。これは同じメモリアドレスを操作しているためです。
- 親クラスのプライベート変数が子クラスで公開されます。
function Parent(){
this.name='father'
this.hobby = ['唱', '跳']
}
Parent.prototype.sayHello = function(){
return this.name
}
function Children(){}
Children.prototype = new Parent()
const c = new Children()
const c2 = new Children()
c.name // father
c.sayHello() // father
c.hobby.push('rap')
c.hobby // ['唱', '跳', 'rap']
c2.hobby // ['唱', '跳', 'rap']
コンビネーション継承#
コンストラクタ + 原型チェーン
利点
- 親クラスのインスタンスプロパティと原型オブジェクトのすべてのプロパティとメソッド(プライベートを除く)を継承します。
- 参照型のプロパティがすべてのインスタンスで共有される問題(原型チェーン継承)を回避します。
欠点
- 親クラスのインスタンスを 2 回呼び出すため、パフォーマンスに影響を与えます。
function Parent(){
this.name='father'
}
Parent.prototype.sayHello = function(){
return this.name
}
function Children(){
Parent.call(this)
}
Children.prototype = new Parent()
Children.prototype.constructor = Children
const c = new Children()
c.name // father
c.sayHello() // father
パラサイト継承#
原型式継承 + オブジェクトの拡張
利点
- 親クラスのインスタンスプロパティと原型オブジェクトのすべてのプロパティとメソッド(プライベートを除く)を継承します。
- 参照型のプロパティがすべてのインスタンスで共有される問題(原型チェーン継承)を回避します。
欠点
- 親クラスのインスタンスを 2 回呼び出すため、パフォーマンスに影響を与えます。
function Parent() {
this.name = 'father'
this.hobby = ['唱', '跳']
}
Parent.prototype.sayHello = function () {
return this.name
}
function Children() {
let parent = new Parent()
parent.sayBye = function Bye() {
return this
}
return parent
}
const c = new Children()
console.log(c.sayHello());
パラサイトコンビネーション継承#
パラサイト継承 + コンビネーション継承
利点
- 親クラスのインスタンスプロパティと原型オブジェクトのすべてのプロパティとメソッド(プライベートを除く)を継承します。
- 参照型のプロパティがすべてのインスタンスで共有される問題(原型チェーン継承)を回避します。
- Parent コンストラクタを 1 回だけ呼び出します
欠点
- 親クラスのインスタンスを 2 回呼び出すため、パフォーマンスに影響を与えます
function _extends(children, parent) {
let parent_prototype = Object.create(parent.prototype)
parent_prototype.constructor = children
children.prototype = parent_prototype
}
function Parent() {
this.name = 'father'
this.hobby = ['唱', '跳']
}
Parent.prototype.sayHello = function () {
return this.name
}
function Children() {
Parent.call(this)
}
_extends(Children, Parent)
const c = new Children()
console.log(c.sayHello()) // father