史上最為詳細的javascript繼承(推薦)
前言
為大家分享js中最常見最詳細的繼承方式,接下來將一下面的幾個維度進行展示說明
文章有點長,請耐心閱讀😁,有什么錯誤理解的地方希望留言指出來
- 產(chǎn)生原因
- 代碼實現(xiàn)
- 基本原理
- 語言實現(xiàn)
- 場景優(yōu)點
- 缺點
繼承方式
- 原型鏈繼承
- 借用構造函數(shù)模式繼承
- 組合繼承
- 原型式繼承
- 寄生式繼承
- 寄生組合
原型鏈繼承
相信小伙伴們都知道到原型鏈繼承(ECMAScript 中描述了原型鏈的概念,并將原型鏈作為實現(xiàn)繼承的主要方法),因為原型鏈繼承非常的強大,但是也有它的缺點,接下來咱們就按照上面的維度看看原型鏈繼承到底是什么鬼
代碼實現(xiàn):(需要兩個構造函數(shù)來完成一個原型鏈繼承)
// SuperType 構造函數(shù)稱為超類
function SuperType (){
this.name='super';
this.friend=[];
this.property = true;
}
SuperType.prototype.getName=function(){
return this.name;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
// SubType 構造函數(shù)稱為子類
function SubType(name,age){
this.name=name;
this.age=age;
this.subproperty = false;
}
SubType.prototype=new SuperType();
SubType.prototype.constrcutor=SubType;
SubType.prototype.getAge=function(){
return this.age;
}
SubType.prototype.getSubValue = function (){
return this.subproperty;
};
var child = new SubType('shiny',12);
console.log(child.getName)//shiny
console.log(child.getAge())//12
圖解部分 屬性

基本原理
使用類似作用域的原型鏈,進行繼承查找
語言實現(xiàn)
定義兩個構造函數(shù),分別為父類(SuperType)、子類(SubType),為了實現(xiàn)子類能夠使用父類的屬性(本身和原型上面的屬性)。重寫子類的原型,讓子類的原型指向父類實例,這樣子類的構造函數(shù)就是父類的實例地址,實現(xiàn)子類可以使用父類的本身和原型上的屬性
優(yōu)點
子類可以通過原型鏈的查找,實現(xiàn)父類的屬性公用與子類的實例
缺點
- 一些引用數(shù)據(jù)操作的時候會出問題,兩個實例會公用繼承實例的引用數(shù)據(jù)類
- 謹慎定義方法,以免定義方法也繼承對象原型的方法重名
- 無法直接給父級構造函數(shù)使用參數(shù)
借用構造函數(shù)模式繼承
雖然原型鏈繼承很強大但是也有他的缺點,借用構造函數(shù)繼承可以解決原型鏈繼承的缺點,開線面的解釋
代碼實現(xiàn):
// 把父類當中一個函數(shù)使用
function SuperType(name){
this.name=name
this.friend=['a','b']
}
SuperType.prototype.getFriend=function(){
return this.firend
}
function SubType(name){
// 執(zhí)行父類函數(shù)
SuperType.call(this,name);
}
var child = new SubType('shiny')
var childRed = new SubType('red')
console.log(child.name)//shiny
console.log(childRed.name)//red
child.firend.push('c')
console.log(child.friend)//a,b,c
console.log(childRed.friend)//a,b
console.log(childRed.getFriend)//undefined
基本原理
使用call apply方法,通過執(zhí)行方法修改tihs (上下文),是的父級的this變成子類實例的this,這樣每個實例都會得到父類的屬性,實現(xiàn)引用屬性備份
使用場景
父類中需要一些子類使用共享的引用類型,并且子類可能會操作父類共享的引用類型
但是父類的非this綁定的屬性和方法是不可以使用的(放在父類prototype的屬性和方法)
語言實現(xiàn)
不要把父類當中構造函數(shù),當中一個函數(shù)來處理這樣更容易理解,在子類的構造函數(shù)中借用父類函數(shù)通過修改this來執(zhí)行,這樣子類的實例包含父類的屬性
優(yōu)點
- 解決了原型鏈繼承的 引用類型操作問題
- 解決了父類傳遞參數(shù)問題
缺點
- 僅僅使用借用構造函數(shù)模式繼承,無法擺脫夠著函數(shù)。方法在構造函數(shù)中定義復用不可談
- 對于超類的原型定義的方法對于子類是不可使用的,子類的實例只是得到了父類的this綁定的屬性
- 考慮到這些缺點,單獨使用借用構造函數(shù)也是很少使用的
組合繼承
上面的兩種繼承方式(原型鏈繼承+借用構造函數(shù)繼承),都有自己優(yōu)缺點,但是他們不是很完美,下面解釋一下組合繼承
代碼實現(xiàn):
function SuperType(name){
this.name=name;
this.firend=['a','b']
}
SuperType.prototype.getName=function(){
return this.name
}
function SubType(name,age){
this.age=age;
SuperType.call(this,name)
}
SubType.prototype=new SuperType();
SubType.prototype.constrcutor = SubType;
SubType.prototype.getAge=function(){
return this.age
}
var childShiny=new SubType('shiny',23);
var childRed = new SubType('red',22);
childShiny.firend.push('c');
childRed.firend.push('d');
console.log(childShiny.getName());
console.log(childShiny.getAge());
console.log(childRed.getName());
console.log(childRed.getAge());
console.log(childRed.friend);//[a,b,d]
console.log(childShiny.friend);//[a,b,c]
基本原理
使用原型鏈的繼承實現(xiàn),通過原型查找功能來滿足原型鏈共享方法
使用借用構造函數(shù)方法,使用實例備份父類共享引用類型備份
使用場景
得到原型鏈繼承和構造函數(shù)繼承的優(yōu)點,是被開發(fā)人員認可的一種繼承方式,但是也有他的缺點
語言實現(xiàn)
定義兩個構造函數(shù),分別為父類(SuperType)、子類(SubType),為了實現(xiàn)子類能夠使用父類的屬性(本身和原型上面的屬性)。重寫子類的原型,讓子類的原型指向父類實例,這樣子類的構造函數(shù)就是父類的實例地址,實現(xiàn)子類可以使用父類的本身和原型上的屬性
不要把父類當中構造函數(shù),當中一個函數(shù)來處理這樣更容易理解,在子類的構造函數(shù)中借用父類函數(shù)通過修改this來執(zhí)行,這樣子類的實例包含父類的屬性
優(yōu)點
- 解決了原型鏈繼承引用類型的實例操作導致引用改變
- 解決了借構造函數(shù)繼承方式的,父類原型子類實例可以使用
缺點
- 父類的構造函數(shù)被實例換了兩次
- 實例會有父類的構造函數(shù)的一些this屬性、子類的構造函數(shù)(prototype)上也有一份實例的上有的屬性
原型式繼承
話說上面的的組合繼承不是已經(jīng)被開發(fā)者認可了嗎,原型式繼承是啥?下面咱們看看原型式繼承是什么樣的。
代碼實現(xiàn):
1 function object(o){
function F(){};
F.prototype=o;
return new F()
}
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var personShiny = object(person);
var personRed = object(person);
console.log(personShiny.name)//Nicholas
console.log(personRed.name)//Nicholas
personShiny.friends.push('red');
personRed.friends.push('shiny');
console.log(personShiny.friends)//["Shelby", "Court", "Van","red","shiny"]
//ECMAScript 5 通過新增 Object.create()方法規(guī)范化了原型式繼承。這個方法接收兩個參數(shù):一
//個用作新對象原型的對象和(可選的)一個為新對象定義額外屬性的對象。在傳入一個參數(shù)的情況下,
//Object.create()與 object()方法的行為相同。
2
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var personShiny = Object.create(person);
var personRed = Object.create(person);
console.log(personShiny.name)//Nicholas
console.log(personRed.name)//Nicholas
personShiny.friends.push('red');
personRed.friends.push('shiny');
console.log(personShiny.friends)//["Shelby", "Court", "Van","red","shiny"]
基本原理
通過Object.create()方法來創(chuàng)建一個有基礎類的實例,這實例的__proto__指向基礎類
使用場景
在不使用構造函數(shù)的情況下,只想讓一個對象與另一個對象保持類似的情況下
語言實現(xiàn)
需要創(chuàng)建一個基礎對象,作為一個新對象的基礎對象,通過object方法或者Object.create方法處理得到一個新實例,這個新實例上的__proto__指向基礎對象
優(yōu)點
再不用創(chuàng)建構造函數(shù)的情況下,實現(xiàn)了原型鏈繼承,代碼量減少一部分
缺點
- 一些引用數(shù)據(jù)操作的時候會出問題,兩個實例會公用繼承實例的引用數(shù)據(jù)類
- 謹慎定義方法,以免定義方法也繼承對象原型的方法重名
- 無法直接給父級構造函數(shù)使用參數(shù)
寄生繼承
咱們看了上面的原型式繼承,其實就是和原型鏈繼承差別不大,只是省去了構造函數(shù)這一部,但是原型式繼承也是有缺點的(不能夠給備份的對象添加屬性),下面寄生繼承來解決。
代碼實現(xiàn):
// 和工廠模式非常類似,創(chuàng)建一個對象,增強一些功能并返回該對象
function createAnother(o){
var clone = Object(o);
clone.sayHi=function(){
console.log('hi')
}
return clone
}
var person = {
name:'shiny',
friends:['a','b']
}
var personShiny = createAnother(person);
console.log(personShiny.sayHi())//Ho
基本原理
備份一個對象,然后給備份的對象進行屬性添加,并返回
使用場景
在考不使用構造函數(shù)的情況下實現(xiàn)繼承,前面示
范繼承模式時使用的 object()函數(shù)不是必需的;任何能夠返回新對象的函數(shù)都適用于此模式
語言實現(xiàn)
類似構造函數(shù),通過一個執(zhí)行方法,里面創(chuàng)建一個對象,為該對象添加屬性和方法,然后返回
優(yōu)點
- 再不用創(chuàng)建構造函數(shù)的情況下,實現(xiàn)了原型鏈繼承,代碼量減少一部分
- 可以給備份的對象添加一些屬性
缺點
類似構造函數(shù)一樣,創(chuàng)建寄生的方法需要在clone對象上面添加一些想要的屬性,這些屬性是放在clone上面的一些私有的屬性
寄生組合繼承
咱們看了上面的組合繼承看上去已經(jīng)很完美了,但是也有缺點(父類被實例化兩次、子類實例和子類的構造函數(shù)都有相同的屬性),寄生組合就是來解決這些問題的
代碼實現(xiàn):
function inheritPrototype({SubType,SuperType}){
const prototype = Object(SuperType.prototype);
prototype.constrcutor=SubType;
SubType.prototype=prototype;
}
function SuperType(name){
this.name=name;
this.friends=['a','b']
}
SuperType.prototype.getName=function(){
return this.name;
}
function SubType(name,age){
this.age=age;
SuperType.call(this,name)
}
inheritPrototype({SubType,SuperType});
SubType.prototype.getAge=function(){
return this.age
}
var SubTypeShiny = new SubType('Shiny',23);
SubTypeShiny .friends.push('c')
var SubTypeRed = new SubType('Red',21);
SubTypeRed .friends.push('d')
console.log(SubTypeShiny.getName())//Shiny
console.log(SubTypeShiny.getAge())//22
console.log(SubTypeShiny.friends)//['a','b','c']
console.log( SubTypeRed.getName())//Red
console.log( SubTypeRed.getAge())//21
console.log( SubTypeRed.friends)//['a','b','d']
基本原理
子類構造函數(shù)內(nèi)通過call、apply方法進行修改父類構造函數(shù)的this和執(zhí)行父類構造函數(shù),使的子類的實例擁有父類構造函數(shù)的一些屬性,
結合子類的原型修改成父類構造函數(shù)的原型,并把父類的原型的constructor指向子類構造函數(shù)
使用場景
在考不使用構造函數(shù)的情況下實現(xiàn)繼承,前面示
范繼承模式時使用的 object()函數(shù)不是必需的;任何能夠返回新對象的函數(shù)都適用于此模式
語言實現(xiàn)
極度類似組合寄生方式,只是修改了子類原型鏈繼承的方式,組合寄生是繼承父類的實例,寄生組合寄生則是通過一子類的原型繼承父類的原型,并把該原型的constructor指向子類構造函數(shù)
優(yōu)點
- 在少一次實例化父類的情況下,實現(xiàn)了原型鏈繼承和借用構造函數(shù)
- 減少了原型鏈查找的次數(shù)(子類直接繼承超類的prototype,而不是父類的實例)
缺點
暫無
下面是組合繼承和寄生組合繼承的原型圖對比

以上所述是小編給大家介紹的javascript繼承詳解整合,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關文章
JavaScript利用canvas實現(xiàn)鼠標跟隨特效
canvas是一個很神奇的玩意兒,比如畫表格、畫海報圖都要用canvas去做。本文就來利用canvas制作個簡單的鼠標跟隨特效,快跟隨小編一起學習一下吧2022-10-10
微信小程序使用uni-app和springboot實現(xiàn)一鍵登錄功能(JWT鑒權)
微信一鍵登錄是指用戶在使用小程序時,可以通過微信賬號進行快速登錄,而無需額外的注冊和密碼設置,這篇文章主要給大家介紹了關于微信小程序使用uni-app和springboot實現(xiàn)一鍵登錄功能的相關資料,需要的朋友可以參考下2023-11-11
prototype.js簡單實現(xiàn)ajax功能示例
這篇文章主要介紹了prototype.js簡單實現(xiàn)ajax功能,結合實例形式分析了prototype.js前臺實現(xiàn)ajax與后臺struts的相關操作技巧,需要的朋友可以參考下2017-10-10

