1、原型鏈: 每個構造函數都有一個原型對象,且有一個指針指向該原型對象(prototype),原型對象都包含一個指向構造函數的指針(constructor),而實例都包含一個指向原型對象的內部指針(proto)。若實例的原型是另一個構造函數的實例,該實例的原型有一個指向另一個原型的指針,層層遞進,構 ...
1、原型鏈:
每個構造函數都有一個原型對象,且有一個指針指向該原型對象(prototype),原型對象都包含一個指向構造函數的指針(constructor),而實例都包含一個指向原型對象的內部指針(proto)。若實例的原型是另一個構造函數的實例,該實例的原型有一個指向另一個原型的指針,層層遞進,構成原型鏈。
原型上的屬性和方法為所有實例所共有,當訪問一個實例屬性時,首先會在實例中搜索該屬性,如果沒有找到該屬性,則會繼續搜索該實例的原型,沿著原型鏈網上搜索,到原型鏈末端或找到為止。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// 子類
function Sub(){
this .property = 'Sub Property'
}
Sub.prototype = new Super()
// 註意這裡new Super()生成的超類對象並沒有constructor屬性,故需添加上
Sub.prototype.constructor = Sub
```缺點:
1 . 包含引用類型的原型屬性會被所有實例共用,一個實例對引用類型屬性進行修改,就是直接修改原型上的該屬性;
2 . 在創建子類型的實例時,沒有辦法給超類型的構造函數傳遞參數。
註意:原型上的引用值屬性和方法使用時均為指針,但構造函數中的不同,不同實例中的同名屬性和方法也不相同!!!
|
2、借用構造函數:
1 2 3 4 5 |
在子類型構造函數中,使用call或apply方法在將來新建的對象上執行父類構造函數
function Sub(){
Super.call( this , '參數' ) //將Super的屬性和方法都複製了一遍
this .property = 'Sub Property'
}
|
- 實例並不是父類的實例,只是子類的實例
- 只能繼承父類的屬性和方法,不能繼承父類原型上的屬性/方法
- 無法實現函數復用(父類上的方法也都複製到最後的實例中,方法不能復用),每個子類都有父類屬性和方法的副本,影響性能
3、組合繼承:
使用原型鏈來實現對原型屬性和方法的繼承,通過借用構造函數來實現對實例屬性的繼承1 2 3 4 5 6 |
function Sub(){
Super.call( this ) //第二次調用Super() Super中的屬性和方法複製進來 this.property = 'Sub Property'
}
Sub.prototype = new Super() //第一次調用Super() Super.prototype上的方法則可以共用
// 註意這裡new Super()生成的超類對象並沒有constructor屬性,故需添加上
Sub.prototype.constructor = Sub
|
4、原型式繼承:
1 2 3 4 5 |
function objectCreate(obj){
function F(){}
F.prototype = obj
return new F()
}
|
5、寄生式繼承:
創建一個僅僅用於封裝繼承過程的函數,然後在內部以某種方式增強對象,最後返回對象1 2 3 4 5 6 7 8 9 10 |
function objectCreate(obj){
function F(){}
F.prototype = obj
return new F()
}
function createSubObj(superInstance){
var clone = objectCreate(superInstance) //任何能夠返回新對象的函數都適用
clone.property = 'Sub Property'
return clone
}
|
6、寄生組合式繼承:
結合寄生式繼承和組合式繼承,完美實現不帶兩份超類屬性的繼承方式。1 2 3 4 5 6 7 8 9 10 |
function inheritPrototype(Super,Sub){
var superProtoClone = Object.create(Super.prototype)
superProtoClone.constructor = Sub
Sub.prototype = Super
}
function Sub(){
Super.call()
Sub.property = 'Sub Property'
}
inheritPrototype(Super,Sub)
|
7、ES6 extends繼承
類的實質是基於原型的和基於構造函數的語法糖
類聲明:class 類名 {類體} ;//類聲明不會提升
類表達式:let 類名 = class 類名 {類體}
extends 來創建子類,並繼承父類 : class 子類名 extends 父類名 {類體}
1 |
class Point{ .... } typeof Point // "function" Point === Point.prototype.constructor // true
|
類的數據類型就是函數,類本身就指向構造函數。
使用的時候,也是直接對類使用new命令,跟構造函數的用法完全一致。
1 2 3 4 |
class Bar{
doStuff(){console.log( 'stuff' );}
} var b = new Bar();
b.doStuff(); // "stuff"
|
構造函數的prototype屬性,在 ES6 的“類”上面繼續存在。事實上,類的所有方法都定義在類的prototype屬性上面。
1 2 3 4 5 6 |
//定義類 class Point{
constructor((x, y){ this .x = x; this .y = y;
}
toString(){ return '(' + this .x + ', ' + this .y + ')' ;
}
}
|
1 2 3 4 5 6 7 8 9 |
class ColorPoint extends Point {
consturctor(x,y,color){
super (x,y); // 調用父類的constructor(x, y)
this .color = color;
}
toString(){
return this .color + " " + super .toString(); //調用父類的toString()方法
}
}
|