1. 程式人生 > 程式設計 >js中實現繼承的五種方法

js中實現繼承的五種方法

借用建構函式

這種技術的基本思想很簡單,就是在子型別建構函式的內部呼叫超型別的建構函式。另外,函式只不過是在特定環境中執行程式碼的物件,因此通過使用apply()和call()方法也可以在新建立的物件上執行建構函式。

function Box(name){
 this.name = name
}
Box.prototype.age = 18

function Desk(name){
 Box.call(this,name) // 物件冒充,物件冒充只能繼承構造裡的資訊
}

var desk = new Desk('ccc')
console.log(desk.name)   // --> ccc
console.log(desk.age)    // --> undefined

從中可以看到,繼承來的只有例項屬性,而原型上的屬性是訪問不到的。這種模式解決了兩個問題,就是可以傳參,可以繼承,但是沒有原型,就沒有辦法複用。

組合繼承

function Box(name){
 this.name = name
}
Box.prototype.run = function (){
 console.log(this.name + '正在執行...')
}

function Desk(name){
 Box.call(this,name) // 物件冒充
}

Desk.prototype = new Box() // 原型鏈

var desk = new Desk('ccc')
console.log(desk.name)   // --> ccc
desk.run()         // --> ccc正在執行...

這種繼承方式的思路是:用使用原型鏈的方式來實現對原型屬性和方法的繼承,而通過借用建構函式來實現對例項屬性的繼承。

原型式繼承

原型式繼承:是藉助原型可以基於已有的物件建立新物件,同時還不必因此建立自定義型別。講到這裡必須得提到一個人,道格拉斯·克羅克福德在2006年寫的一篇文章《Prototype inheritance in Javascript》(Javascript中的原型式繼承)中給出了一個方法:

function object(o) {   //傳遞一個字面量函式
 function F(){}     //建立一個建構函式
 F.prototype = o;    //把字面量函式賦值給建構函式的原型
 return new F()     //最終返回出例項化的建構函式
}

看如下的例子:

function obj(o) {
 function F (){}
 F.prototype = o;
 return new F()
}

var box = {
 name: 'ccc',age: 18,family: ['哥哥','姐姐']
}

var box1 = obj(box)
console.log(box1.name)   // --> ccc
box1.family.push('妹妹')
console.log(box1.family)  // --> ["哥哥","姐姐","妹妹"]

var box2 = obj(box)
console.log(box2.family)  // --> ["哥哥","妹妹"]

因為上述的程式碼的實現邏輯跟原型鏈繼承很類似,所以裡面的引用陣列,即family屬性被共享了。

寄生式繼承

function obj(o) {
 function F (){}
 F.prototype = o;
 return new F()
}
function create(o){
 var clone = obj(o)   // 通過呼叫函式建立一個新物件
 clone.sayName = function(){   // 以某種方式來增強這個物件
  console.log('hi')
 }
 return clone   // 返回這個物件
}

var person = {
 name: 'ccc',friends: ['aa','bb']
}

var anotherPerson = create(person)
anotherPerson.sayName()   // --> hi

這個例子中的程式碼基於person返回一個新物件————anotherPerson。新物件不僅具有person的所有屬性和方法,而且還有自己的sayHi()方法。在主要考慮物件而不是自定義型別和建構函式的情況下,寄生式繼承也是一種有用的模式。使用寄生式繼承來為物件新增函式,會由於不能做到函式複用而降低效率,這一點與建構函式模式類似。

寄生組合式繼承

前面說過,組合繼承是Javascript最常用的繼承模式,不過,它也有自己的不足。組合繼承最大的問題就是無論什麼情況下,都會呼叫過兩次超型別建構函式:一次是在建立子型別原型的時候,另一次是在子型別建構函式內部。沒錯,子型別最終會包含超型別物件的全部例項屬性,但我們不得不在呼叫子型別建構函式時重寫這些屬性,再來看一下下面的例子:

function SuperType(name){
 this.name = name;
 this.colors = ['red','black']
}
SuperType.prototype.sayName = function (){
 console.log(this.name)
}
function SubType(name,age){
 SuperType.call(this,name) // 第二次呼叫SuperType
 this.age = age
}

SubType.prototype = new SuperType() // 第一次呼叫SuperType
SubType.prototype.constructor = SubType
SubType.prototype.sayAge = function (){
 console.log(this.age)
}

第一次呼叫SuperType建構函式時,SubType.prototype會得到兩個屬性:name和colors。他們都是SuperType的例項屬性,只不過現在位於SubType的原型中。當呼叫SubType建構函式時,又會呼叫一次SuperType建構函式,這個一次又在新物件上建立了例項屬性name和colors。於是,這兩個屬性就遮蔽了原型中的兩個同名屬性。即有兩組name和colors屬性:一組在例項上,一組在原型上。這就是呼叫兩次SuperType建構函式的結果。解決這個問題的方法就是————寄生組合式繼承。
所謂寄生組合式繼承,即通過借用建構函式來繼承屬性,通過原型鏈的混成形式來繼承方法。其背後的基本思路是:不必為了制定子型別的原型而呼叫超型別的建構函式,我們所需要的無非就是超型別原型的一個副本而已。本質上,就是使用寄生式繼承來繼承超型別的原型,然後再將結果指定給子型別的原型。寄生組合式繼承的基本模式如下:

function object(o) {
 function F (){}
 F.prototype = o;
 return new F()
}
function inheritPtototype(subType,superType){
 var prototype = object(superType.prototype) // 建立物件
 prototype.constructor = subType       // 增強物件
 subType.prototype = prototype        // 指定物件
}

function SuperType(name){
 this.name = name
 this.colors = ['red','white']
}

SuperType.prototype.sayName = function(){
 console.log(this.name)
}

function SubType(name,name)
 this.age = age
}

inheritPtototype(SubType,SuperType)

SubType.prototype.sayAge = function(){
 console.log(this.age)
}

var instance = new SubType('ccc',18)

instance.sayName()   // --> ccc
instance.sayAge()    // --> 18
console.log(instance)

控制檯打印出的結構:

js中實現繼承的五種方法

詳細的圖解:

js中實現繼承的五種方法

這個例子的高效率提現在它值呼叫了一次SuperType建構函式,並且因此避免了在SubType.prototype上面建立不必要的、多餘的屬性。與此同時,原型鏈還能保持不變;因此,還能夠正常使用instanceof和isPrototypeOf()。這也是很多大廠用的繼承方式。

以上就是js中實現繼承的五種方法的詳細內容,更多關於js 實現繼承的資料請關注我們其它相關文章!