大綱:
- 原型鏈
- 借用構(gòu)造函數(shù)
- 組合繼承
- 原型式繼承
- 寄生式繼承
- 寄生組合式繼承
1、原型鏈:
- 什么是原型鏈烁设?
原型鏈的基本思想是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法
function Father(){
this.hair = "black";
}
Father.prototype.getHair = function(){
return this.hair
}
function Son(){
this.eyes = "blue";
}
//Son繼承了Father
//沒有使用Son的默認原型檬某,而是換了一個新原型撬腾。本質(zhì)是重寫原型
Son.prototype = new Father();
Son.prototype.getEyes = function(){
return this.eyes;
}
//Son的實例對象,并不是函數(shù)
var Gson = new Son();
console.log(Gson.getHair()); //black
console.log(Gson.getEyes()); //blue
console.log(Son.prototype.getHair()); //black
- 繼承的實現(xiàn):
1恢恼、通過創(chuàng)建Father的實例賦值給Son.prototype實現(xiàn)的繼承民傻。
2、實現(xiàn)的本質(zhì)是重寫原型對象场斑,以一個新類型的實例取代漓踢。
3、即存在Father實例中的所有屬性和方法漏隐,現(xiàn)在也存在Son.prototype中了
- 捋一下Object喧半,F(xiàn)ather和Son,Gson之間的關(guān)系:
1青责、所有的引用類型都默認繼承了Object挺据,繼承的方式也是原型鏈
2、Gson是Son的實例對象脖隶,通過var Gson = new Son()扁耐。此時Gson里的__ proto__指向Son的prototype。
console.log(Gson.__proto__ == Son.prototype); //true
3产阱、Son繼承了Father婉称,通過Son.prototype = new Father()。此時Son的prototype作為實例對象,他的__ proto__屬性指向Father的prototype
console.log(Son.prototype.__proto__ == Father.prototype); //true
4王暗、由于Son實現(xiàn)繼承的本質(zhì)是重寫原型對象悔据。Son.prototye的constructor屬性已經(jīng)不再指向Son。而是指向Father
console.log(Son.prototype.constructor == Son); //false
console.log(Son.prototype.constructor == Father); //true
5俗壹、所有函數(shù)的默認原型都是Object的實例科汗,所以默認的原型都有__ proto__屬性指向Object.prototye。這也是所有自定義類型都會繼承toString(),valueOf()等默認方法的根本原因
console.log(Father.prototype.__proto__ == Object.prototype); //true
console.log(Son.prototype.__proto__ == Object.prototype); //true
6策肝、Son繼承了Father肛捍,F(xiàn)ather繼承了Object。而Gson是Son的實例對象之众。
-
簡單圖示:
image.png - 注意:
不能使用對象字面量創(chuàng)建原型方法拙毫。因為會改變constructor的指向,會導(dǎo)致原型鏈被切斷
- 問題:
1棺禾、包含引用類型值的原型缀蹄。
2、在創(chuàng)建子類型的實例時膘婶,不能向超類型的構(gòu)造函數(shù)中傳遞參數(shù)
function Father(){
this.colors = ["black","blue","red"]
}
function Son(){}
Son.prototype = new Father();
var Gson1 = new Son();
Gson1.colors.push("yellow");
console.log(Gson1.colors); //["black", "blue", "red", "yellow"]
var Gson2 = new Son();
console.log(Gson2.colors); //["black", "blue", "red", "yellow"]
//這是因為Father的實例對象是Son.prototye缺前,而prototype屬性上的方法和屬性都會共享
2、借用構(gòu)造函數(shù):(偽造對象或經(jīng)典繼承)
- 基本思想:
在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù)悬襟。
- 例子:
實際上是在Son的要創(chuàng)建的新實例(Gson1,Gson2)環(huán)境下調(diào)用了Father衅码。
1、使用Father.call()方法脊岳,可以解決原型鏈包含引用類型值的原型的問題
function Father(){
this.colors = ["black","blue","red"]
}
function Son(){
Father.call(this) //this指向Son的新實例對象
}
var Gson1 = new Son();
Gson1.colors.push("yellow");
console.log(Gson1.colors); //["black", "blue", "red", "yellow"]
var Gson2 = new Son();
console.log(Gson2.colors); //["black", "blue", "red"]
2逝段、解決原型鏈傳遞參數(shù)問題
function Father(name){
this.name = name
}
function Son(){
//繼承了Father,并傳遞參數(shù)
Father.call(this,"Tom");
//實例屬性,要在繼承之后添加
this.age = 36;
}
var Gson = new Son();
console.log(Gson.name); //Tom
console.log(Gson.age); //36
3割捅、組合繼承:(偽經(jīng)典繼承)
是將原型鏈和借用構(gòu)造函數(shù)相結(jié)合奶躯。
1、使用原型鏈實現(xiàn)對原型屬性和方法的繼承
2亿驾、通過借用構(gòu)造函數(shù)來實現(xiàn)對實例屬性的繼承
- 優(yōu)點:
1嘹黔、通過在原型上定義方法實現(xiàn)了函數(shù)復(fù)用
2、還能保證每個實例都有它自己的屬性
- 例子:
function Father(){
this.colors= ["black","blue","red"];
}
function Son(name,age){
//繼承屬性
Father.call(this);
this.name = name;
this.age = age;
}
//繼承方法
Son.prototype = new Father();
Son.prototype.constructor = Son;
Son.prototype.isAge = function(){
console.log(this.age);
}
Son.prototype.isName = function(){
console.log(this.name);
}
var Gson1 = new Son("lili",11);
Gson1.colors.push("yellow")
console.log(Gson1.colors); //["black", "blue", "red", "yellow"]
console.log(Gson1.name); //lili
console.log(Gson1.age); //11
var Gson2 = new Son("bree",20);
console.log(Gson2.colors); //["black", "blue", "red"]
console.log(Gson2.name); //bree
console.log(Gson2.age); //20
- 問題:
1莫瞬、無論什么情況下儡蔓,都會調(diào)用兩次超類型構(gòu)造函數(shù)。
2疼邀、一次是在創(chuàng)建子類型原型的適合
3喂江、另一次是在子類型構(gòu)造函數(shù)內(nèi)部
4、原型式繼承
- 思想:
借助原型可以基于已有的對象創(chuàng)建新對象檩小,同時不必因此創(chuàng)建自定義類型开呐。
function obj(o){ //本質(zhì)上obj()對傳入的對象執(zhí)行了一次淺復(fù)制
function F(){}
F.prototype = o;
return new F();
}
var lion = {
name:"lion",
friends:["giraffe","elephant","rabbit"]
}
//將lion傳入obj中烟勋,返回一個新對象规求。
//這個新對象的原型指向lion
//即lion上的屬性被加到新對象上的原型上了
var fox = obj(lion);
fox.name = "fox";
fox.friends.push("cat"); //friends屬性被共享了
var wolf = obj(lion);
wolf.name = "wolf";
wolf.friends.push("tiger");
console.log(lion.friends); //["giraffe", "elephant", "rabbit", "cat", "tiger"]
- Object.create方法:
1筐付、只有一個參數(shù)時,用法和obj()一樣
2阻肿、有兩個參數(shù)時瓦戚,用法如下:
var lion = {
name:"lion",
friends:["giraffe","elephant","rabbit"]
}
//類似描述符修改屬性
var fox = Object.create(lion,{
name:{
value:"fox"
}
});
console.log(fox.name);
- 優(yōu)點:
當(dāng)沒有必要創(chuàng)建構(gòu)造函數(shù),只想讓一個對象與另一個對象保持類似的情況下丛塌,原型式繼承是完全可以勝任的较解。
- 缺點:
包含引用類型值的屬性始終會共享相應(yīng)的值。
5赴邻、寄生式繼承:
- 本質(zhì):
1印衔、創(chuàng)建一個僅用于封裝繼承過程的函數(shù),該函數(shù)在內(nèi)部以某種方式來增強對象姥敛,最后再想真的是他做了所有工作一樣返回對象
2奸焙、與寄生構(gòu)造函數(shù)和工廠模式類似
function obj(o){
function F(){}
F.prototype = o;
return new F();
}
function clo(z){ //封裝繼承過程
var clone = obj(z); //繼承函數(shù)
clone.sayHi = function(){
alert('hi')
};
return clone //返回
}
var lion = {
name:"lion",
friends:["giraffe","elephant","rabbit"]
}
var zoo = clo(lion);
zoo.sayHi() //hi
console.log(zoo.name); //lion
console.log(zoo.friends); //["giraffe", "elephant", "rabbit"]
- 注意:
obj()不是必需的,任何能夠返回新對象的函數(shù)都適用于此模式
- 缺點:
使用寄生式繼承來為對象添加函數(shù)彤敛,會由于不能做到函數(shù)復(fù)用而降低效率与帆,與構(gòu)造函數(shù)模式類似。
6墨榄、寄生組合式繼承
- 概念:
通過借用構(gòu)造函數(shù)來繼承屬性玄糟,通過原型鏈的混成形式來繼承方法
- 基本思路:
不必為了指定子類型的原型而調(diào)用超類型的構(gòu)造函數(shù),我們所需要的無非就是超類型原型的一個副本而已
- 本質(zhì):
使用寄生式繼承來繼承超類型的原型袄秩,然后再將結(jié)果指定給子類型的原型阵翎。
- 例子:
function obj(o){
function F(){}
F.prototype = o;
return new F();
}
function inher(son,father){
var pro = obj(father.prototype); //創(chuàng)建對象
pro.constructor = son; //增強對象
son.prototype = pro; //指定對象
}
function Father(name){
this.name = name;
this.colors = ["black","blue","yellow"]
}
Father.prototype.isName = function(){
alert(this.name)
}
function Son(name,age){
Father.call(this,name); //只調(diào)用一次Father
this.age=age;
}
inher(Son,Father);
Son.prototype.isAge=function(){
alert(this.age)
}
- 優(yōu)點:
1、只調(diào)用一次Father構(gòu)造函數(shù)播揪,也避免了再Son.prototype上面創(chuàng)建不必要的屬性贮喧。
2、原型鏈能保持不變
3猪狈、是引用類型最理想的繼承方式