1.原型鏈繼承
每個(gè)構(gòu)造函數(shù)都有一個(gè)原型對(duì)象,原型對(duì)象都包含一個(gè)指向構(gòu)造函數(shù)的指針(constructor),而每個(gè)實(shí)例都包含一個(gè)指向原型對(duì)象的內(nèi)部指針(proto).
實(shí)現(xiàn)原型鏈繼承的基本模式:
//定義第一個(gè)類型
function FirstFun() {
this.num = 1
}
//第一個(gè)類型定義方法
FirstFun.prototype.getFirstNum = function() {
console.log(this.num);
}
//定義第二個(gè)類型
function SecondFun() {
this.numb = 2
}
//繼承第一個(gè)類型
SecondFun.prototype = new FirstFun()
//第二個(gè)類型定義方法
SecondFun.prototype.getSecondNum = function() {
console.log(this.numb)
}
//第二個(gè)類型添加實(shí)例
var numbers = new SecondFun()
//調(diào)用實(shí)例繼承來的方法
numbers.getFirstNum()
//結(jié)果打印為:1
原型鏈繼承需注意:
(1)別忘記默認(rèn)原型:
所有函數(shù)的默認(rèn)原型都是object的實(shí)例。因此所有默認(rèn)原型都會(huì)包含一個(gè)內(nèi)部指針,指向object.prototype,這也是所有自定義類型都會(huì)繼承toString()、valueOf()等默認(rèn)方法的根本原因。
(2)確定原型和實(shí)例的方法
第一個(gè):instanceof
alert(numbers instanceof Object) //true
alert(numbers instanceof FirstFun) //true
alert(numbers instanceof SecondFun) //true
第二個(gè):isPrototypeOf()
alert(Object.prototype.isPrototype(numbers)) //true
alert(FirstFun.prototype.isPrototype(numbers)) //true
alert(SecondFun.prototype.isPrototype(numbers)) //true
(3)謹(jǐn)慎定義方法:
給原型添加方法的代碼一定要在替換原型的語(yǔ)句之后。
還有,再通過原型鏈實(shí)現(xiàn)繼承時(shí),不能使用對(duì)象字面量創(chuàng)建原型方法,因?yàn)檫@樣會(huì)重寫原型鏈。
(4)原型鏈存在的問題:
第一個(gè)問題:
引用類型值的原型屬性,會(huì)被所有實(shí)例共享,這正是為什么要在構(gòu)造函數(shù)中,而不是在原型上定義屬性的原因。
function Obj1() {
this.colors = ["red","blue","yellow"]
}
function Obj2() {
}
Obj2.prototype = new Obj1()
var test1 = new Obj2()
test1.colors.push("green")
console.log(test1.colors) //["red","blue","yellow","green"]
var test2 = new Obj2
console.log(test2.colors) //["red","blue","yellow","green"]
第二個(gè)問題:
沒辦法在不影響所有對(duì)象實(shí)例的情況下,向超類型的構(gòu)造函數(shù)中傳遞參數(shù)。
2.借用構(gòu)造函數(shù)(有時(shí)候也叫偽造對(duì)象或者經(jīng)典繼承)
基本思想:在子類型構(gòu)造函數(shù)內(nèi)部調(diào)用超類型構(gòu)造函數(shù)。
函數(shù)只不過是在特定環(huán)境中執(zhí)行代碼的對(duì)象,因此通過使用apply()或者call()方法也可以在(將來)新創(chuàng)建的對(duì)象上執(zhí)行構(gòu)造函數(shù)。
function Obj1() {
this.colors = ["red","blue","yellow"]
}
function Obj2() {
Obj1.apply(this)
}
var test1 = new Obj2()
test1.colors.push("green")
console.log(test1.colors) //["red","blue","yellow","green"]
var test2 = new Obj2
console.log(test2.colors) //["red","blue","yellow"]
借用構(gòu)造函數(shù)繼承優(yōu)缺點(diǎn):
(1)優(yōu)點(diǎn):可以在子類型構(gòu)造函數(shù)中向超類型構(gòu)造函數(shù)傳遞參數(shù)。
function Obj1(name) {
this.name = name
}
function Obj2() {
//繼承了Obj1,同時(shí)傳遞了參數(shù)
Obj1.call(this, "Peter")
this.age = 28
}
var test1 = new Obj2()
console.log(test1.name) //Peter
console.log(test1.age) //28
(2)缺點(diǎn):方法都在構(gòu)造函數(shù)中定義,因此函數(shù)復(fù)用就無從談起。
3.組合繼承(也叫偽經(jīng)典繼承)
思路:
通過使用原型鏈實(shí)現(xiàn)対原型屬性和方法的繼承,通過借用構(gòu)造函數(shù)實(shí)現(xiàn)對(duì)實(shí)例屬性的繼承。
function Obj1(name) {
this.name = name
this.colors = ['aaa','bbb','ccc']
}
Obj1.prototype.sayName = function() {
console.log(this.name);
}
function Obj2(name, age) {
//繼承屬性
Obj1.call(this, name)
// 添加新的屬性
this.age = age
}
//繼承方法
Obj2.prototype = new Obj1()
//彌補(bǔ)因重寫原型失去的constructor屬性
Obj2.prototype.constructor = Obj2
//添加新的方法
Obj2.prototype.sayAge = function() {
console.log(this.age);
}
var test1 = new Obj2('Peter', 28)
test1.colors.push('ddd')
console.log(test1.colors)
test1.sayName()
test1.sayAge()
var test2 = new Obj2('Greo', 33)
console.log(test2.colors)
test2.sayName()
test2.sayAge()

優(yōu)點(diǎn):組合繼承避免了原型鏈與借用構(gòu)造函數(shù)繼承的缺陷,融合入了他們的優(yōu)點(diǎn)。
缺點(diǎn):無論在什么情況下,都會(huì)調(diào)用兩次超類型構(gòu)造函數(shù)。一次在創(chuàng)建子類型原型的時(shí)候,一次在子類型構(gòu)造函數(shù)內(nèi)部。
4.寄生式繼承
基本思路:創(chuàng)建一個(gè)僅用于封裝繼承過程的函數(shù),該函數(shù)在內(nèi)部以某種方式來增強(qiáng)對(duì)象,最后再像真的是它做了所有工作一樣返回對(duì)象。
寄生式繼承模式:
function createAnother(original) {
var clone = Object.create(original)
clone.sayHi = function() {
alert("hi")
}
return clone
}
var person = {
name:"Daiv",
friends:["one","two","three"]
}
var anotherPerson = createAnother(person)
anotherPerson.sayHi()
5.寄生組合式繼承
所謂寄生組合式繼承,即通過借用構(gòu)造函數(shù)來繼承屬性,通過原型鏈的混成形式來繼承方法。
基本思路:
不必為了指定子類型的原型而調(diào)用超類型的構(gòu)造函數(shù),我們需要的無非就是超類型原型的一個(gè)副本而已。本質(zhì)上,就是使用寄生式繼承來繼承超類型的原型,然后再將結(jié)果指定給子類型的原型。
基本模式如下:
//obj1是子類型結(jié)構(gòu),obj2是超類型結(jié)構(gòu)
function inheritPrototype(obj1, obj2){
//第一步:創(chuàng)建超類型原型副本
var prototype = object(obj2.prototype)
//第二步:為創(chuàng)建的副本添加constructor屬性,彌補(bǔ)因?yàn)橹貙懺投サ哪J(rèn)的constructor屬性
prototype.constructor = obj1
//第三步:將新創(chuàng)建的對(duì)象(即副本),賦值給子類型的原型
obj1.prototype = prototype
}
function SuperType(name){
this.name = name
this.colors = ["red","blue","green"]
}
SuperType.prototype.sayName = function(){
alert(this.name)
}
function SubType(name,age){
SuperType.call(this,name)
this.age = age
}
inheritPrototype(SubType,SuperType)
SubType.prototype.sayAge = function(){
alaert(this.age)
}