1. 原型鏈
原型鏈是實現(xiàn)繼承的主要方法,它是利用原型,讓一個引用類型繼承另一個應用類型的屬性和方法。
- 實現(xiàn)原型鏈的基本模式:
//定義父類構(gòu)造函數(shù)
function Person(){
this.PersonImprint = "from_Person";
}
Person.prototype.PersonSay = function(){
console.log(this.PersonImprint);
}
//定義子類構(gòu)造函數(shù)
function Son(){
this.SonImprint = "from_Son";
}
//初始化子類構(gòu)造函數(shù)原型為父類實例----實現(xiàn)繼承父類屬性和方法。
Son.prototype = new Person();
var sonExample = new Son();
console.log(sonExample.PersonImprint) //from_Person
sonExample.PersonSay()) //from_Person
console.log(sonExample.SonImprint) //from_Son
上面將
Son的原型指向了Person的實例,這樣Son的新原型不僅具有Person的實例所擁有的全部屬性和方法,而且其原型內(nèi)部還有一個指針,指向了Person的原型。
最終得出:sonExample指向Son的原型,而Son的原型又指向Person的原型。
//實例檢測
sonExample instanceof Son //true
sonExample instanceof Person //true
sonExample instanceof Object //true
//原型檢測
sonExample.prototype.isPrototypeOf(Son) //true
sonExample.prototype.isPrototypeOf(Person) //true
sonExample.prototype.isPrototypeOf(Object) //true
由于原型鏈的關(guān)系,可以說
sonExample是Son、Person、Object中任何一個類型的實例。
-
原型鏈注意事項
- 子類有時候需求覆蓋超類型中的某個方法,或者添加超類型中的某個不存在的屬性或方法,這種操作代碼,需要放在繼承(
替換原型)之后。- 實現(xiàn)繼承時,不能以對象字面量創(chuàng)建原型,這樣會重寫原型。
-
原型鏈的問題
- 依據(jù)原型的“共享”這一點看出的,雖然說是通過原型來實現(xiàn)繼承,但是其原型其實是另一個類型的實例。這樣原來的實例屬性也就成為了現(xiàn)在原型的屬性了。
- 在創(chuàng)建子類的實例時,不能向超類的構(gòu)造函數(shù)中傳遞參數(shù)。
2. 借用構(gòu)造函數(shù)
這種方法的基本思想相當簡單,即在子類型構(gòu)造函數(shù)的內(nèi)部通過call()和apply()調(diào)用超類型構(gòu)造函數(shù)。
function SuperType(){
this.colors = ["red","blue","green"];
}
function SubType(){
SuperType.call(this)
}
var instance1 = new SubType();
instance1.colors.push("black");
//["red","blue","green","black"];
var instance2 = new SubType();
//["red","blue","green"];
上面子類的構(gòu)造方法
SubType中“借調(diào)”了超類SuperType的構(gòu)造函數(shù),在創(chuàng)建新的實例過程中,運行了超類構(gòu)造函數(shù),這樣一來子類就擁有了超類函數(shù)中定義的所有屬性了。
-
傳遞參數(shù)
function SuperType(name){
this.name = name;
}
function SubType(){
SuperType.call(this,"余嘉")
}
var instance1 = new SubType()
-
借用構(gòu)造函數(shù)的弊端
- 如果是僅僅借用構(gòu)造函數(shù)(超類),那么構(gòu)造函數(shù)(超類)的存在意義就是問題所在,因為方法都在超類中定義,因此構(gòu)造函數(shù)的復用就無從談起了。
- 超類中給原型定義的方法,對于子類型而言是看不見的。
3. 組合繼承(原型鏈+借用構(gòu)造函數(shù))
將原型鏈和借用構(gòu)造函數(shù)組合使用,利用兩者的長處。
function superType(name){
this.name = name;
this.colors = ["red","blue","green"];
}
superType.prototype.sayName = function(){
console.log(this.name)
}
funtionc subType(name,age){
superType.call(this,name);
this.age = age;
}
subType.prototype = new superType();
subType.prototype.constructor = subType;
subType.prototype.sayAge = function(){
console.log(this.age)
};
var instance1 = new subType("余嘉",27);
//subType {name: "余嘉", colors: Array(3), age: 27};
instance1.sayAge() // 27
instance1.sayName() // "余嘉"
組合繼承避免了原型鏈和借用構(gòu)造模式的缺陷,融合了他們的優(yōu)點,是項目中最常用的繼承模式。
4. 原型式繼承(Object.create( obj , newKeyobj))
借助原型可以基于已有的對象(實例)創(chuàng)建新對象,同事還不必因此創(chuàng)建自定義類型。
function create(0){
function F(){}
F.prototype = o;
return new F();
}
在
create()函數(shù)內(nèi)部,先創(chuàng)建一個臨時性的構(gòu)造函數(shù),然后將傳入的對象O作為這個構(gòu)造函數(shù)的原型(淺復制),然后返回臨時構(gòu)造的實例。
es5新增的Object.create(obj , newKeyobj)規(guī)范這個原型式繼承,它接受兩個參數(shù)
- (必須的)obj 作為新對象的原型對象
- (可選的)為新對象定義額外屬性對象,和
Object.difineProperties()方法的參數(shù)一樣。
var person = {
name : "余嘉",
friends:["cat","dog"]
}
var otherPerson1 = Object.create(person);
otherPerson1.name = "小微";
otherPerson1.friends.push("蘿卜");
var otherPerson2 = Object.create(person);
otherPerson1.name = "達令";
otherPerson1.friends.push("白菜");
console.log(person.friends);
//["cat","dog","蘿卜","白菜"]
var person = {
name : "余嘉",
friends:["cat","dog"]
}
var otherPerson1 = Object.create(person,{
name:{
value:"小微"
}
});
- “共享”的利與弊,永遠是原型模式的烙印。
5. 寄生式繼承
寄生式繼承的思路與寄生構(gòu)造函數(shù)和工廠模式類似,創(chuàng)建一個經(jīng)用于封裝繼承過程的函數(shù)。
function createAnother(original){
var clone = Object(original);
clone.sayHi = function(){
console.log("Hi")
};
return clone
}
調(diào)用
var person = {
name:"余嘉",
friends:["Shelby", "Court", "Van"]
}
var anotherPerson = createAnother(person);
anotherPerson.sayHi();
//Hi