js繼承的6種方式詳解
原型鏈繼承
原型鏈繼承是ECMAScript的主要繼承方式。其基本思想就是通過原型繼承多個(gè)引用類型的屬性和方法。什么是原型鏈?每個(gè)構(gòu)造函數(shù)都會有一個(gè)原型對象,調(diào)用構(gòu)造函數(shù)創(chuàng)建的實(shí)例會有一個(gè)指針__proto__指向原型對象,這個(gè)原型可能是另一個(gè)類型的實(shí)例,所以內(nèi)部可能也有一個(gè)指針指向另一個(gè)原型,然后就這樣形成了一條原型鏈。
代碼: function SuperType() { this.property = true; } SuperType.prototype.getSuperValue = function() { return this.property; }; function SubType() { this.subproperty = false; } // 繼承SuperType SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function () { //注意 不能通過對象字面量的方式添加新方法,否則上一行無效 return this.subproperty; }; let instance = new SubType(); console.log(instance.getSuperValue()); // true
缺點(diǎn)
1.如果父類實(shí)例的屬性是引用類型的時(shí)候,其實(shí)父類的實(shí)例屬性會成為子類的原型屬性,子類創(chuàng)建的所有實(shí)例都會共享這些方法,修改一個(gè)實(shí)例的這個(gè)屬性,其他實(shí)例的屬性也會被修改
2.子類型在實(shí)例化時(shí)不能給父類型的構(gòu)造函數(shù)傳參
構(gòu)造函數(shù)繼承
為了解決原型包含引用值導(dǎo)致的繼承問題,出現(xiàn)了一中'盜用構(gòu)造函數(shù)'的技術(shù)流行起來,也被稱為'對象偽裝'或'經(jīng)典繼承',思路就是在子類構(gòu)造函
數(shù)中調(diào)用父類構(gòu)造函數(shù)。可以使用call() apply()的方法以新創(chuàng)建的對象為上下文執(zhí)行函數(shù)
function SuperType(name) { this.colors = ["red","blue","green"]; this.name = name; } function SubType(name) { SuperType.call(this,name); } let instance1 = new SuperType('小明') let instance2 = new SuperType('小白') instance1.colors .push('yellow') console.log(instance1) //{name:"小明",colors:["red","blue","green","yellow"]...} console.log(instance2) //{name:"小白",colors:["red","blue","green"]...} //可以傳遞參數(shù) 也修復(fù)了引用的問題 可以繼承多個(gè)構(gòu)造函數(shù)屬性(call多個(gè))
缺點(diǎn):
1.只能在構(gòu)造函數(shù)中調(diào)用方法 函數(shù)不能重用 就是每次子類生成實(shí)例的時(shí)候都會生成一次屬性和方法
2. 子類無法訪問到父類原型上的方法
組合繼承
綜合了原型鏈和構(gòu)造函數(shù),將兩者的優(yōu)點(diǎn)集中了起來?;镜乃悸肥鞘褂迷玩溊^承原型上的屬性和方法,而通過構(gòu)造函數(shù)繼承實(shí)例屬性。這樣既可以把方法定義在原型上以實(shí)現(xiàn)重用,又可以讓每個(gè)實(shí)例都有自己的屬性。
function SuperType(name){ this.name = name; this.colors = ["red","blue","green"]; } SuperType.prototype.sayName = function() { console.log(this.name); }; function SubType(name, age){ // 繼承屬性 第二次調(diào)用 SuperType.call(this, name); this.age = age; } // 繼承方法 第一次調(diào)用 SubType.prototype = new SuperType(); SubType.prototype.sayAge = function() { console.log(this.age); }; let instance1 = new SubType("Nicholas", 29); instance1.colors.push("black"); console.log(instance1.colors); //["red,blue,green,black"] instance1.sayName(); // "Nicholas"; instance1.sayAge(); // 29 let instance2 = new SubType("Greg", 27); console.log(instance2.colors); // ["red,blue,green"] instance2.sayName(); // "Greg"; instance2.sayAge(); // 27 //可以繼承父類原型上的屬性,可以傳參,可復(fù)用。 每個(gè)新實(shí)例引入的構(gòu)造函數(shù)屬性是私有的
缺點(diǎn)
調(diào)用了兩次父類構(gòu)造函數(shù) 比較耗內(nèi)存
原型式繼承
即使不自定義類型也可以通過原型實(shí)現(xiàn)對象之間的信息共享。
function object(person) { function F() {} F.prototype = person return new F() } let person = { name:'小明', colors:['red','blue'] } let person1 = object(person) person1.colors.push('green') let person2 = object(person) person1.colors.push('yellow') console.log(person) //['red','blue','green','yellow']
適用環(huán)境: 你有一個(gè)對象,想在它的基礎(chǔ)上再創(chuàng)建一個(gè)新對象。你需要把這個(gè)對象先傳給object() ,然后再對返回的對象進(jìn)行適當(dāng)修改。類似于 Object.create()只傳第一個(gè)參數(shù)的時(shí)候,本質(zhì)上就是對傳入的對象進(jìn)行了一次淺復(fù)制,缺點(diǎn)就是新實(shí)例的屬性都是后面添加的,無法復(fù)用
寄生式繼承
與原型式繼承比較接近的一種繼承方式是寄生式繼承,類似于寄生構(gòu)造函數(shù)和工廠模式:創(chuàng)建一個(gè)實(shí)現(xiàn)繼承的函數(shù),以某種方式增強(qiáng)對象,然后返回這個(gè)對象。
function object(person) { function F() {} F.prototype = person return new F() } function createAnother(original){ let clone = object(original); // 通過調(diào)用函數(shù)創(chuàng)建一個(gè)新對象 clone.sayHi = function() { // 以某種方式增強(qiáng)這個(gè)對象 console.log("hi"); }; return clone; // 返回這個(gè)對象 }
寄生式繼承同樣適合主要關(guān)注對象,而不在乎類型和構(gòu)造函數(shù)的場景。
缺點(diǎn):通過寄生式繼承給對象添加函數(shù)會導(dǎo)致函數(shù)難以重用,與構(gòu)造函數(shù)模式類似
寄生式組合繼承
最常用的繼承方式,也是最佳的,組合繼承會調(diào)用兩次父類構(gòu)造函數(shù),存在效率問題。其實(shí)本質(zhì)上子類原型最終是要包含父類對象的所有實(shí)例屬性,子類構(gòu)造函數(shù)只要在執(zhí)行時(shí)重寫自己的原型就行了?;舅悸肥遣煌ㄟ^調(diào)用父類構(gòu)造函數(shù)給子類原型賦值,而是取得父類原型的一個(gè)副本。說到底就是使用寄生式繼承來繼承父類原型,然后將返回的新對象賦值給子類原型。
//核心代碼 function object(person) { function F(params) {} F.prototype = person return new F() } function inheritPrototype(SubType,SuperType) { let prototype = object(SuperType.prototype) //生成一個(gè)父類原型的副本 //重寫這個(gè)實(shí)例的constructor prototype.constructor = SubType //將這個(gè)對象副本賦值給 子類的原型 SubType.prototype = prototype } function SuperType(name) { this.name = name; this.colors = ["red","blue","green"]; } SuperType.prototype.sayName = function() { console.log(this.name); }; function SubType(name, age) { SuperType.call(this, name); this.age = age; } //調(diào)用inheritPrototype函數(shù)給子類原型賦值,修復(fù)了組合繼承的問題 inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function() { console.log(this.age); };
總結(jié)
到此這篇關(guān)于js繼承的6種方式的文章就介紹到這了,更多相關(guān)js繼承方式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
微信小程序?qū)崿F(xiàn)歷史搜索功能的全過程(h5同理)
最近在使用微信小程序開發(fā)的時(shí)候遇到了一個(gè)需求,需要實(shí)現(xiàn)歷史搜索記錄的功能,所以下面這篇文章主要給大家介紹了關(guān)于微信小程序?qū)崿F(xiàn)歷史搜索功能(h5同理)的相關(guān)資料,需要的朋友可以參考下2022-12-12Bootstrap 實(shí)現(xiàn)表格樣式、表單布局的實(shí)例代碼
這篇文章主要介紹了Bootstrap 實(shí)現(xiàn)表格樣式、表單布局的實(shí)例代碼,需要的朋友可以參考下2018-12-12JavaScript獲取IP獲取的是IPV6 如何校驗(yàn)
項(xiàng)目中遇到了關(guān)于IPV6的一些問題,特意做一個(gè)專輯說明一下,希望能夠幫助有需要的同學(xué)!2016-06-06js為鼠標(biāo)添加右擊事件防止默認(rèn)的右擊菜單彈出
本文為大家介紹下如何為使用js為鼠標(biāo)添加右擊事件防止默認(rèn)的右擊菜單彈出,感興趣的朋友可以參考下,希望對大家有所幫助2013-07-07JS實(shí)現(xiàn)彈出下載對話框及常見文件類型的下載
JS要實(shí)現(xiàn)下載功能,一般都是這么幾個(gè)過程:生成下載的URL,動態(tài)創(chuàng)建一個(gè)A標(biāo)簽,并將其href指向生成的URL,然后觸發(fā)A標(biāo)簽的單擊事件,這樣就會彈出下載對話框,從而實(shí)現(xiàn)了一個(gè)下載的功能2017-07-07