一篇文章教你JS函數(shù)繼承
一. 前言:
Hello,大家最近過得好嗎,😃。函數(shù)繼承是在JS里比較基礎(chǔ)也是比較重要的一部分,而且也是面試中常常要問到的。下面帶你快速了解JS中有哪幾種是經(jīng)常出現(xiàn)且必須掌握的繼承方式。掌握下面的內(nèi)容面試也差不多沒問題啦~
當(dāng)然,這需要一定的原型鏈基礎(chǔ),對原型鏈不熟悉的可以看我這篇文章👉:速識js原型鏈。
二.原型鏈繼承:
原型鏈繼承的要點(diǎn)在于父類的實(shí)例作為子類的原型。直接看下面這個例子:
// 父函數(shù) Person function Person(name, age) { // 定義一些屬性 this.name = name; this.age = age; this.nature = ["auroras", "wind", "moon"]; } // 定義Person原型上的一個方法 Person.prototype.sayLove = function () { console.log(this.name + " like " + this.nature[0]); }; // 子函數(shù) Jack function Jack() {} // 父類的實(shí)例作為子類的原型 (-------------------實(shí)現(xiàn)核心--------------------------) Jack.prototype = new Person();
現(xiàn)在我們創(chuàng)建兩個Jack 的實(shí)例,測試看是否實(shí)現(xiàn)了繼承Person:
var jack1 = new Jack(); var jack2 = new Jack(); jack2.nature[0] = "sea"; jack1.sayLove(); jack2.sayLove(); console.log(jack1.nature); console.log(jack2.nature);
看運(yùn)行結(jié)果確實(shí)繼承了,能執(zhí)行sayLove方法。但有甚多缺點(diǎn),創(chuàng)建Jack實(shí)例的時候傳遞不了參數(shù)name和age,而且不同實(shí)例間nature引用類型屬性相互影響,一個改變那都改變:
三.借用構(gòu)造函數(shù)繼承(對象偽裝):
核心在于“盜用構(gòu)造函數(shù)”(constructor stealing)。在子類構(gòu)造函數(shù)中調(diào)用父類構(gòu)造函數(shù)。因?yàn)楫吘购瘮?shù)就是在特定上下文中執(zhí)行代碼的簡單對象,所以可以使用apply()和call()方法以新創(chuàng)建的對象為上下文執(zhí)行構(gòu)造函數(shù)。它能解決原型鏈繼承中傳參數(shù)和引用類型屬性沖突。還是直接看例子:
// 父函數(shù) Person function Person(name, age) { // 定義一些屬性 this.name = name; this.age = age; this.nature = ["auroras", "wind", "moon"]; } // 定義Person原型上的一個方法 Person.prototype.sayLove = function () { console.log(this.name + " like " + this.nature[0]); }; // 子函數(shù) Lucy function Lucy(name, age) { // 通過call把this指向Lucy,相當(dāng)于拷貝了一份父函數(shù) Person 里的內(nèi)容(---------實(shí)現(xiàn)核心--------------) Person.call(this, name, age); } //給子函數(shù)原型上也定義一個方法 Lucy.prototype.syaName = function () { console.log("My name is " + this.name); };
現(xiàn)在我們創(chuàng)建兩個Lucy 的實(shí)例,測試看是否實(shí)現(xiàn)了繼承Person:
var lucy1 = new Lucy("lucy1", "20"); var lucy2 = new Lucy("lucy2", "22"); lucy2.nature[0] = "sea"; console.log(lucy1.name); console.log(lucy1.nature); console.log(lucy2.nature); lucy1.syaName(); lucy2.syaName(); lucy1.sayLove();
結(jié)果看可以繼承了,能傳參數(shù),引用類型屬性也不互相影響,但是缺點(diǎn)顯而易見,可以看到報錯,無法使用父類的原型上的方法sayLove。
四.組合繼承:
組合繼承就是結(jié)合了原型鏈繼承和借用構(gòu)造函數(shù)繼承兩者的核心實(shí)現(xiàn)的一種繼承方法,既能傳遞參數(shù),引用類型屬性也互不影響,同時子類也能獲取得到父類的方法。這也是目前比較常用的繼承方式。直接看例子:
// 父函數(shù) Person function Person(name, age) { // 定義一些屬性 this.name = name; this.age = age; this.nature = ["auroras", "wind", "moon"]; } // 定義Person原型上的一個方法 Person.prototype.sayLove = function () { console.log(this.name + " like " + this.nature[0]); }; // 子函數(shù)Lisa function Lisa(name, age) { // 通過call把this指向Lisa,相當(dāng)于拷貝了一份父函數(shù) Person 里的內(nèi)容(------實(shí)現(xiàn)核心-----------) Person.call(this, name, age); } // 父類的實(shí)例作為子類的原型 (--------------實(shí)現(xiàn)核心-------------------) Lisa.prototype = new Person(); //小知識點(diǎn),這里是讓Lisa的constructor重新指向Lisa,不然因?yàn)長isa的原型為Person實(shí)例,constructor會指向Person Lisa.prototype.constructor = Lisa;
現(xiàn)在我們創(chuàng)建兩個Lisa 的實(shí)例,測試看是否實(shí)現(xiàn)了繼承Person:
var lisa1 = new Lisa("lisa1", "20"); var lisa2 = new Lisa("lisa2", "21"); lisa2.nature[0] = "sea"; console.log(lisa1.name); console.log(lisa1.nature); console.log(lisa2.nature); lisa1.sayLove(); lisa2.sayLove();
可以看到基本上實(shí)現(xiàn)了我們繼承的功能。也修補(bǔ)了原型鏈和借用構(gòu)造函數(shù)繼承的缺點(diǎn)。但是呢,它還是有一個小缺點(diǎn),就是可以看到在代碼注釋實(shí)現(xiàn)核心那,兩次都調(diào)用了Person,那么Lisa原型上和實(shí)例上有了兩份相同的屬性,那就會多少有一些性能浪費(fèi)。
五.寄生組合繼承:
其實(shí)寄生組合繼承和組合繼承差不多的,就是多了一個解決組合繼承上原型和實(shí)例產(chǎn)生兩份相同屬性的缺點(diǎn)。解決核心是我們既然只是想要子類原型賦值為父類原型,那沒必要new一個父類實(shí)例。直接創(chuàng)造一個新對象,它值為父類的原型,再將它賦值給子類原型就行了。
其中用到Object.create(proto,[propertiesObject])這個方法創(chuàng)建一個新對象。相當(dāng)于新對象的__proto__為其參數(shù)proto。當(dāng)然Object.create可能低版本ie沒有,所以下面也自定義封裝了Object.create方法,當(dāng)然只是簡單封裝。直接看例子:
// 父函數(shù) Person function Person(name, age) { // 定義一些屬性 this.name = name; this.age = age; this.nature = ["auroras", "wind", "moon"]; } // 定義Person原型上的一個方法 Person.prototype.sayLove = function () { console.log(this.name + " like " + this.nature[0]); }; // 子函數(shù) Andy function Andy(name, age) { Person.call(this, name, age); } // 如果沒有 Object.create()方法,簡單封裝下 if (!Object.create) { Object.create = function (proto) { function Temp() {} Temp.prototype = proto; return new Temp(); }; } // 調(diào)用Object.create方法,新建一對像,其__proto__為Person.prototype,并賦值給 Andy.prototype (-------實(shí)現(xiàn)核心----------) Andy.prototype = Object.create(Person.prototype); //修改constructor指向 Andy.prototype.constructor = Andy;
現(xiàn)在我們創(chuàng)建兩個Andy的實(shí)例,測試看是否實(shí)現(xiàn)了繼承Person:
console.log(Andy.prototype.__proto__ === Person.prototype); var andy1 = new Andy("andy1", "20"); var andy2 = new Andy("andy2", "21"); andy2.nature[0] = "sea"; console.log(andy1.name); console.log(andy1.nature); console.log(andy2.nature); andy1.sayLove(); andy2.sayLove();
完美運(yùn)行:
六.class繼承:
ES6出了class語法糖之后,就可以通過class定義類并實(shí)現(xiàn)類的繼承。直接看例子:
//定義一個父類 Animal class Animal { //這里constructor指向類本身,跟es5行為一樣的 constructor(name) { this.name = name; } likeEat() { console.log(this.name + " like eat " + this.food); } } //定義一個子類 Dog ,通過 extends 繼承父類Animal class Dog extends Animal { constructor(name, food) { //通過super(屬性名)繼承父類屬性 super(name); this.food = food; } likeEat() { //通過super.+父類方法 實(shí)現(xiàn)繼承父類方法 super.likeEat(); } }
new一個Dog實(shí)例,測試看看,Dog是否繼承了Animal:
var jinmao = new Dog("jinmao", "bone"); console.log(jinmao.name); jinmao.likeEat();
可以看到完美實(shí)現(xiàn)了:
七.總結(jié):
方法 | 優(yōu)點(diǎn) | 缺點(diǎn) |
---|---|---|
原型鏈繼承 | 能繼承父原型上屬性方法等等… | 無法傳參、引用類型屬性沖突等等… |
借用構(gòu)造函數(shù)繼承 | 可以傳參,引用類型屬性不沖突等等… | 無法繼承父原型上方法等等… |
組合繼承 | 有上面兩種的優(yōu)點(diǎn),并解決其缺點(diǎn) | 調(diào)用兩次父實(shí)例產(chǎn)生兩份相同屬性等等… |
寄生組合繼承 | 有上面三種優(yōu)點(diǎn),并解決其缺點(diǎn) | 可能不太直觀等等… |
class繼承 | es6新語法,簡潔直觀等等… | 低版本ie不支持es6等等… |
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
?javascript學(xué)數(shù)組中的foreach方法和some方法
這篇文章主要介紹了?javascript學(xué)數(shù)組中的foreach方法和some方法,文章相關(guān)內(nèi)容和代碼詳細(xì),具有一定的參考價值,需要的小伙伴可以參考一下,希望對你的學(xué)習(xí)有所幫助2022-03-03JS驗(yàn)證輸入的是否是數(shù)字及保留幾位小數(shù)問題
這篇文章主要介紹了JS驗(yàn)證輸入的是否是數(shù)字及保留幾位小數(shù)問題,非常不錯,具有一定的參考借鑒價值,需要的朋友參考下吧2018-05-05BetterScroll 在移動端滾動場景的應(yīng)用
BetterScroll 是一款重點(diǎn)解決移動端各種滾動場景需求的開源插件( GitHub地址 ),非常不錯,下面腳本之家小編給大家分享BetterScroll 在移動端滾動場景的應(yīng)用,一起看看吧2017-09-09JavaScript函數(shù)的一些注意要點(diǎn)小結(jié)及js匿名函數(shù)
本文給大家總結(jié)了javascript函數(shù)的一些注意要點(diǎn)及js匿名函數(shù),主要知識點(diǎn)有:函數(shù)的基本語法、函數(shù)的參數(shù)、函數(shù)的重載相關(guān)知識,對本文感興趣的朋友一起學(xué)習(xí)吧2015-11-11javascript正則表達(dá)式之分組概念與用法實(shí)例
這篇文章主要介紹了javascript正則表達(dá)式之分組概念與用法,結(jié)合實(shí)例形式分析了javascript正則表達(dá)式分組的功能、定義與使用方法,需要的朋友可以參考下2016-06-06