JavaScript中的幾種繼承方法示例
1.原型鏈繼承
原理:子類原型指向父類例項物件實現原型共享,即Son.prototype = new Father()。
這裡先簡單介紹下原型
js中每個物件都有一個__proto__屬性,這個屬性指向的就是該物件的原型。js中每個函式都有一個prototype屬性,這個屬性指向該函式作為建構函式呼叫時建立的例項的原型。原型物件上有一個constructor屬性,指向建立該物件的建構函式,該屬性不可列舉。
var obj = {}; obj.__proto__ === Object.prototype; //true console.log(Object.prototype.constructor) // ƒ Object
當我們訪問一個物件的屬性或者方法時,如果找不到,則會通過原型向上尋找,若原型上也未找到,則會去原型的原型上面去找。比如我要呼叫obj.toString方法時,在自身並未找到toString方法,則會去原型上去尋找,即在Object.prototype上去尋找,找到後執行該方法。
var obj = {}; obj.toString(); obj.__proto__.toString(); //obj.__proto__和Object.prototype指向的是一個物件,自然就能訪問Object.prototype上的toString方法啦
注意:原型鏈的終點是null,使用bind方法返回的函式沒有prototype屬性。
var obj = {}; function fn(){}; fn.bind(obj).prototype; // undefined Object.prototype.__proto__; // null
原型連結繼承
function Father(age){ this.age = age; this.color = ['red','pink'] } Father.prototype.sayHello = function(){ console.log('hello') } function Son(sex){ this.sex = sex } console.log(Son.prototype.constructor) // ƒ Son Son.prototype = new Father(15) // 原型鏈繼承關鍵 var son = new Son('男') son.color.push('black') var son2 = new Son('女') son.sayHello() // hello son.sayHello === son2.sayHello //true console.log(son2.color) // ['red','pink','black'] console.log(Son.prototype.constructor) // ƒ Father
可以看到通過原型鏈實現繼承,原型上引用型別的值會被所有例項共享。子類的constructor指向會發生改變,而且在建立子類例項時不可以向父類建構函式傳遞引數。可以手動把子類constructor屬性指回其建構函式。
//寫法一 Son.prototype.constructor = Son // 這種寫法有點缺點,它會讓constructor屬性變的可以列舉。 //寫法二 Object.defineProperty(Son.prototype,'constructor',{ enumerable:false,// 設定不可列舉 value:Son })
2.建構函式繼承
原理:在子類建構函式中通過apply或者call呼叫父類建構函式來繼承屬性或方法。
function Father(name){ this.color = ['red'] this.sayHello = function(){ console.log('hello') } } Father.prototype.sayName = function(){ console.log('zs') } function Son(num,name){ Father.call(this,name) //實現繼承的關鍵程式碼 this.num = num } var son = new Son(10,'zs') var son2 = new Son(15,'ls') son.color.push('pink') console.log(son2.color) // ['red'] son.sayName() //報錯 son.sayName is not a function console.log(son.sayHello === son2.sayHello) //false
可以看出通過建構函式實現繼承,解決了原型鏈繼承不能向父類傳參以及引用型別值共享的問題。但這種繼承方法卻不能訪問父類建構函式原型上的方法和屬性,而且定義在父類建構函式中的方法也不能複用。
3.組合式繼承
組合繼承,有時候也叫偽經典繼承,它是將原型鏈繼承和建構函式繼承結合到一起的一種繼承模式。實現思路是通過原型鏈實現對原型屬性和方法的繼承,通過借用建構函式實現對例項屬性的繼承。
function Father(name){ this.color = ['red'] } Father.prototype.sayName = function(){ console.log('zs') } function Son(num,name) //繼承例項屬性 this.num = num } Son.prototype = new Father() //繼承原型上屬性 Son.prototype.constructor = Son var son = new Son(10,'ls') son.color.push('pink') console.log(son.color,son2.color) //['red','pink'] ['red'] son.sayName() // zs
組合式繼承避免了原型鏈繼承和建構函式繼承的缺點,融合了它們的優點,成為JavaScript中常用的一種繼承模式。
4.寄生式繼承
寄生式繼承與工廠模式類似,一般用來繼承物件。即建立一個封裝繼承的函式,在函式內部複製一份該物件,對複製的物件進行處理,返回複製的物件。
function createAnother(obj){ var clone = Object.create(obj) clone.name = 'zs' clone.sayHello = function(){ console.log('hello') } return clone } var obj = {age:15} var newObj = createAnother(obj) // 15 console.log(newObj.name) // zs newObj.sayHello() // hello
5.寄生組合式繼承
前面說到過組合式繼承是Javascript中最常用的繼承模式,不過這種模式也有自己的不足,它會呼叫兩次父類建構函式。第一次是在將子類原型指向父類例項的時候,第二次是在子類建構函式中呼叫的。
function Father(name){ this.name = name } function Son(num,name){ Father.call(this,name) // 第二次呼叫 } Son.prototype = new Father('ls') // 第一次呼叫 var son = new Son(10,'zs') console.log(son)
在第一次呼叫的時候,Son.prototype會繼承name這個屬性,第二次呼叫時,例項物件會繼承name。當我們獲取例項物件的name屬性時因為例項物件上有該屬性,所以是不會去原型上去尋找的,相當於例項物件上的name屬性把原型上的name屬性給遮蔽掉了,所以原型上的這個屬性是多餘的。
為了解決這個問題,就有了寄生組合式繼承。主要思路就是建立一個函式完成原型鏈繼承和constructor的指向問題,然後通過建構函式繼承屬性。
// 複製一個父類的原型指向,將子類的原型指向複製的父類原型,達到不用呼叫父類建構函式就能繼承其原型上的方法的效果。 function inherit(Sup,Sub){ var prototype = Object.create(Sup.prototype) Sub.prototype = prototype prototype.constructor = Sub } function Father(name){ this.name = name } function Son(name){ Father.call(this,name) } inherit(Father,Son) var son = new Son('zs') console.log(son)
以上就是JavaScript中常用的幾種繼承方式啦。
到此這篇關於JavaScript中的幾種繼承方法的文章就介紹到這了,更多相關JavaScript繼承方法內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!