創: 前端二牛
借用構造函數繼承解決了原型鏈數據共享和無法向超類型傳遞參數的問題,但自身的缺陷是不能使用超類型原型中定義的方法。組合繼承是將原型鏈繼承和借用構造函數繼承組合到一起,從而發揮二者之長的一種繼承模式,也被稱作偽經典繼承。背后的思路是使用原型鏈實現原型屬性和方法的繼承,使用構造函數來實現實例屬性的繼承。這樣既通過在原型上定義方法實現了函數的復用,又能保證每個實例都有它自己的屬性。
下面來看一個例子:
在這個例子中首先聲明了一個 Human函數,然后使用原型模式定義了 sayName方法。聲明了 Person函數,通過構造函數模式繼承了 Human,但是這只能繼承 Human中聲明的屬性,也就是實例屬性,沒辦法繼承原型屬性和方法,于是緊接著將 Person的原型賦值為 Human的一個實例,通過原型鏈實現原型屬性和方法的繼承,然后添加了一個子類型特有的方法 sayName,這就是組合繼承。測試使用,結果既可以通過構造函數向超類型傳遞參數,也沒有 colors被共享的問題,同時還可以使用超類型原型上定義的方法 sayName,可以說非常完美。
組合繼承避免了原型鏈和借用構造函數的缺陷,融合了它們的優點,成為JavaScript中最常用的繼承模式。而且,因為使用了原型鏈, instanceOf和 isPrototypeOf也能夠識別基于組合繼承創建的對象。
為粉絲提出的建議 ,上一篇給大家講了call()和apply()的基本使用方法 。這一篇文章中為大家講解js繼承 , 內容中也會結合call()方法的使用 , 算是趁熱打鐵 。 沒有學過call()方法的要先學一下上篇文章哦 ~call()和apply()的用法
說到繼承 ,童靴們最先想到的可能是原型繼承了。如下:
如上方法中通過b.prototype=new a("hkk",23)實現b的原型繼承了a , 但是這種方法有一個弊端 ,相信童靴們也能看出來 , 就是之后如果實例化函數b , 每個對象的屬性name和age值都是一樣的 ,都為“hkk”和23 。為了優化這個弊端 , 所以出現了組合繼承。
組合繼承結合call()方法和原型的方式實現繼承 , 如下:
在函數b中使用call方法把父類a的屬性繼承 , 然后使用原型把父類原型上面的方法繼承過來 。此時b也擁有了a上面的所有屬性 ,所以就可以在實例化的時候為每個實例對象傳入不同的參數 。當然,這種方法也有一個不足之處,就是使用call()方法已經拿到父類所有的屬性 ,后面再使用原型時也會有父類所有屬性 ,如下:
如上,盡管子類的屬性會覆蓋掉原型上面的屬性 ,可畢竟重復的東西看不來不太好。為了改善這個不足 , 我們需要封裝一個方法 ,如下:
封裝如上函數 ,獲取到傳入函數的原型 , 在調用時傳入父類函數 ,如下:
此時 ,子類b的原型就只會繼承到父類a原型上面的方法 , 而不是重復的基礎父類a的屬性了。
最后還要注意的是 , 使用原型繼承會使子類的構造器指向父類a ,所以我們需要用如下方法讓b原型的構造器指向b.
寫在這里 ,一個較完善的繼承也就完成了。下面附上完整的例子:
你必須非常努力,才能看起來毫不費力 !
關注小白前端,持續收到文章推送~
組合繼承是原性鏈繼承和構造函數繼承的合體,它汲取了二者各自的有點,同時又互相補充了各自的弱點,是一種應用十分廣泛的JavaScript繼承模式。下面分別從原性鏈繼承、構造函數繼承分別開始介紹,最后介紹二者的結合——組合繼承。
一、原型鏈
利用原型讓一個引用類型繼承另一個引用類型的屬性和方法
每個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個指向原型對象的內部指針。
實現原性鏈的基本模式:
function SuperType(){ //定義了一個父函數 this.property=true; } SuperType.prototype.getSuperValue=function(){ //給父函數的原型鏈上添加一個getSuperValue的函數 returnthis.property; } function Subtype(){ //定義一個子函數 this.subproperty=false; } SubType.prototype=new SuperType(); //SubType實現了繼承SuperType SubType.prototype.getSubValue=function(){ //給子函數添加方法 return this.subproperty; } var instance=new SubType(); // 實例instance繼承子函數 alert(instance.getSuperValue());
最后的結果:intance指向SubType的原型,而SubType的原型又指向SuperType的原型,SuperType繼承了Object,所有函數的默認原型都是Object的實例
問題:會產生引用類型值的問題
比如,創建了一個子類的實例,如果對子類實例的屬性進行了修改,那么創建其他子類的時候都會收到影響,代碼如下:
function SuperType(){ this.colors=[“red”, “blue”, “green”]; } function SubType(){ } SubType.prototype=new SuperType(); var instance1=new SubType(); instance1.colors.push(“black”); alert(instance1.colors); //red, blue, green, black var instance2=new SubType(); alert(instance2.colors); //red, blue, green, black
以上結果說明會影響其他實例的屬性值
二、借用構造函數
在子類型構造函數的內部調用超類型構造函數
function SuperType(){ // 定義一個父函數 this.colors=[“red”, “blue”, “green”]; } function SubType{}( // 定義一個子函數 SuperType.call(this); // 繼承了父函數 } var instance1=new SubType(); // 實例instance1繼承子函數 instance1.colors.push(“black”); alert(intance1.colors); //red,blue,green,black var instance2=new SubType(); // 實例instance2繼承子函數 alert(instance2.colors); //red,blue,green
使用該方法可以在子類構造函數中向超類型構造函數傳遞參數,如下:
function SuperType(name){ // 定義父函數 this.name=name; } function SubType(){ // 定義子函數 SuperType.call(this,“Nicholas”); //傳入參數,利用這個參數初始化父類構造函數中的name this.age=29; } var instance=new SubType(); // 實例instance繼承子函數 alert(instance.name); //Nicholas alert(instance.age); //29
問題:不方便復用
三、組合式繼承
使用原型鏈實現對原型屬性和方法的繼承,而通過借用構造函數來實現對實例屬性的繼承
示例代碼:
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); //繼承SuperType的一部分,this指SubType, this.age=age; //自己新定義的屬性age也可以進行賦值 } SubType.prototype=new SuperType(); //利用原型繼承,可以使用父類的方法 SubType.prototype.sayAge=function(){ //定義SubType特有的新方法 alert(this.age); } var instance1=new SubType(“Martin”, 10); instance1.colors.push(“black”); alert(instance1.colors); //red,blue,green,black instance1.sayName(); //Martin instance1.sayAge(); //10 var instance2=new SubType(“Greg”, 27); alert(instance2.colors); //red,blue,green instance2.sayName(); //Greg instance2.sayAge(); //27
綜合例子:
function Person(name, age) { this.name=name; this.age=age; } Person.prototype.hi=function() { console.log('Hi, my name is' + this.name) + ",I'm" + this.age + 'years old now.'; }; Person.prototype.LEGS_NUM=2; Person.prototype.ARMS_NUM=2; Person.prototype.walk=function() { console.log(this.name + "is walking..."); } function Student(name, age, className) { Person.call(this, name, age); this.className=className; } Student.prototype=Objectcreate(Person.prototype); Student.prototype.constructor=Student; Student.prototype.hi=function() { console.log('Hi, my name is' + this.name + ", I'm" + this.age + "years old now, and from" + this.className + "."); } Student.prototype.learn=function(subject) { console.log(this.name + 'is learing' + subject + 'at' + this.className + '.'); } // test var bosn=new Student('Bosn', 27, 'Class 3,Grade 2'); bosn.hi(); // Hi, my name is Bosn, I'm 27 years old now and from Class3,Grage 2 bosn.LEGS_NUM; // 2 bosn.walk(); // Bosn is walking bosn.learn('math'); // Bosn is learning math t Class3, Grade 2.
學習從來不是一個人的事情,要有個相互監督的伙伴,想要學習或交流前端問題的小伙伴可以私信“學習”小明獲取web前端入門資料,一起學習,一起成長!
*請認真填寫需求信息,我們會在24小時內與您取得聯系。