大綱:
- 原型鏈
- 借用構(gòu)造函數(shù)
- 組合繼承
- 原型式繼承
- 寄生式繼承
- 寄生組合式繼承
1、原型鏈:
- 什么是原型鏈?
原型鏈的基本思想是利用原型讓一個(gè)引用類(lèi)型繼承另一個(gè)引用類(lèi)型的屬性和方法
function Father(){
this.hair = "black";
}
Father.prototype.getHair = function(){
return this.hair
}
function Son(){
this.eyes = "blue";
}
//Son繼承了Father
//沒(méi)有使用Son的默認(rèn)原型,而是換了一個(gè)新原型。本質(zhì)是重寫(xiě)原型
Son.prototype = new Father();
Son.prototype.getEyes = function(){
return this.eyes;
}
//Son的實(shí)例對(duì)象,并不是函數(shù)
var Gson = new Son();
console.log(Gson.getHair()); //black
console.log(Gson.getEyes()); //blue
console.log(Son.prototype.getHair()); //black
- 繼承的實(shí)現(xiàn):
1、通過(guò)創(chuàng)建Father的實(shí)例賦值給Son.prototype實(shí)現(xiàn)的繼承。
2、實(shí)現(xiàn)的本質(zhì)是重寫(xiě)原型對(duì)象,以一個(gè)新類(lèi)型的實(shí)例取代。
3、即存在Father實(shí)例中的所有屬性和方法,現(xiàn)在也存在Son.prototype中了
- 捋一下Object,F(xiàn)ather和Son,Gson之間的關(guān)系:
1、所有的引用類(lèi)型都默認(rèn)繼承了Object,繼承的方式也是原型鏈
2、Gson是Son的實(shí)例對(duì)象,通過(guò)var Gson = new Son()。此時(shí)Gson里的__ proto__指向Son的prototype。
console.log(Gson.__proto__ == Son.prototype); //true
3、Son繼承了Father,通過(guò)Son.prototype = new Father()。此時(shí)Son的prototype作為實(shí)例對(duì)象,他的__ proto__屬性指向Father的prototype
console.log(Son.prototype.__proto__ == Father.prototype); //true
4、由于Son實(shí)現(xiàn)繼承的本質(zhì)是重寫(xiě)原型對(duì)象。Son.prototye的constructor屬性已經(jīng)不再指向Son。而是指向Father
console.log(Son.prototype.constructor == Son); //false
console.log(Son.prototype.constructor == Father); //true
5、所有函數(shù)的默認(rèn)原型都是Object的實(shí)例,所以默認(rèn)的原型都有__ proto__屬性指向Object.prototye。這也是所有自定義類(lèi)型都會(huì)繼承toString(),valueOf()等默認(rèn)方法的根本原因
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的實(shí)例對(duì)象。
-
簡(jiǎn)單圖示:
image.png - 注意:
不能使用對(duì)象字面量創(chuàng)建原型方法。因?yàn)闀?huì)改變constructor的指向,會(huì)導(dǎo)致原型鏈被切斷
- 問(wèn)題:
1、包含引用類(lèi)型值的原型。
2、在創(chuàng)建子類(lèi)型的實(shí)例時(shí),不能向超類(lèi)型的構(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"]
//這是因?yàn)镕ather的實(shí)例對(duì)象是Son.prototye,而prototype屬性上的方法和屬性都會(huì)共享
2、借用構(gòu)造函數(shù):(偽造對(duì)象或經(jīng)典繼承)
- 基本思想:
在子類(lèi)型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類(lèi)型構(gòu)造函數(shù)。
- 例子:
實(shí)際上是在Son的要?jiǎng)?chuàng)建的新實(shí)例(Gson1,Gson2)環(huán)境下調(diào)用了Father。
1、使用Father.call()方法,可以解決原型鏈包含引用類(lèi)型值的原型的問(wèn)題
function Father(){
this.colors = ["black","blue","red"]
}
function Son(){
Father.call(this) //this指向Son的新實(shí)例對(duì)象
}
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ù)問(wèn)題
function Father(name){
this.name = name
}
function Son(){
//繼承了Father,并傳遞參數(shù)
Father.call(this,"Tom");
//實(shí)例屬性,要在繼承之后添加
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、使用原型鏈實(shí)現(xiàn)對(duì)原型屬性和方法的繼承
2、通過(guò)借用構(gòu)造函數(shù)來(lái)實(shí)現(xiàn)對(duì)實(shí)例屬性的繼承
- 優(yōu)點(diǎn):
1、通過(guò)在原型上定義方法實(shí)現(xiàn)了函數(shù)復(fù)用
2、還能保證每個(gè)實(shí)例都有它自己的屬性
- 例子:
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
- 問(wèn)題:
1、無(wú)論什么情況下,都會(huì)調(diào)用兩次超類(lèi)型構(gòu)造函數(shù)。
2、一次是在創(chuàng)建子類(lèi)型原型的適合
3、另一次是在子類(lèi)型構(gòu)造函數(shù)內(nèi)部
4、原型式繼承
- 思想:
借助原型可以基于已有的對(duì)象創(chuàng)建新對(duì)象,同時(shí)不必因此創(chuàng)建自定義類(lèi)型。
function obj(o){ //本質(zhì)上obj()對(duì)傳入的對(duì)象執(zhí)行了一次淺復(fù)制
function F(){}
F.prototype = o;
return new F();
}
var lion = {
name:"lion",
friends:["giraffe","elephant","rabbit"]
}
//將lion傳入obj中,返回一個(gè)新對(duì)象。
//這個(gè)新對(duì)象的原型指向lion
//即lion上的屬性被加到新對(duì)象上的原型上了
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、只有一個(gè)參數(shù)時(shí),用法和obj()一樣
2、有兩個(gè)參數(shù)時(shí),用法如下:
var lion = {
name:"lion",
friends:["giraffe","elephant","rabbit"]
}
//類(lèi)似描述符修改屬性
var fox = Object.create(lion,{
name:{
value:"fox"
}
});
console.log(fox.name);
- 優(yōu)點(diǎn):
當(dāng)沒(méi)有必要?jiǎng)?chuàng)建構(gòu)造函數(shù),只想讓一個(gè)對(duì)象與另一個(gè)對(duì)象保持類(lèi)似的情況下,原型式繼承是完全可以勝任的。
- 缺點(diǎn):
包含引用類(lèi)型值的屬性始終會(huì)共享相應(yīng)的值。
5、寄生式繼承:
- 本質(zhì):
1、創(chuàng)建一個(gè)僅用于封裝繼承過(guò)程的函數(shù),該函數(shù)在內(nèi)部以某種方式來(lái)增強(qiáng)對(duì)象,最后再想真的是他做了所有工作一樣返回對(duì)象
2、與寄生構(gòu)造函數(shù)和工廠模式類(lèi)似
function obj(o){
function F(){}
F.prototype = o;
return new F();
}
function clo(z){ //封裝繼承過(guò)程
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()不是必需的,任何能夠返回新對(duì)象的函數(shù)都適用于此模式
- 缺點(diǎn):
使用寄生式繼承來(lái)為對(duì)象添加函數(shù),會(huì)由于不能做到函數(shù)復(fù)用而降低效率,與構(gòu)造函數(shù)模式類(lèi)似。
6、寄生組合式繼承
- 概念:
通過(guò)借用構(gòu)造函數(shù)來(lái)繼承屬性,通過(guò)原型鏈的混成形式來(lái)繼承方法
- 基本思路:
不必為了指定子類(lèi)型的原型而調(diào)用超類(lèi)型的構(gòu)造函數(shù),我們所需要的無(wú)非就是超類(lèi)型原型的一個(gè)副本而已
- 本質(zhì):
使用寄生式繼承來(lái)繼承超類(lèi)型的原型,然后再將結(jié)果指定給子類(lèi)型的原型。
- 例子:
function obj(o){
function F(){}
F.prototype = o;
return new F();
}
function inher(son,father){
var pro = obj(father.prototype); //創(chuàng)建對(duì)象
pro.constructor = son; //增強(qiáng)對(duì)象
son.prototype = pro; //指定對(duì)象
}
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)點(diǎn):
1、只調(diào)用一次Father構(gòu)造函數(shù),也避免了再Son.prototype上面創(chuàng)建不必要的屬性。
2、原型鏈能保持不變
3、是引用類(lèi)型最理想的繼承方式
