阿里巴巴技術(shù)文章分享 Javascript繼承機(jī)制的實(shí)現(xiàn)
Javascript作為一門腳本語言,在設(shè)計(jì)之初并沒有考慮到面向?qū)ο蟮奶匦?。即便到了?dāng)今這個(gè)遍布現(xiàn)代瀏覽器的年代,各種Javascript 框架/庫如雨后春筍般地瘋狂生長,Javascript中連個(gè) class 關(guān)鍵字都沒有。如果你要編寫一個(gè)類,你還得借助于function,至于繼承、重載什么的,就別奢望了。
可是,沒有繼承,日子怎么過?。侩y道把所有的共有邏輯都拷貝一遍,實(shí)現(xiàn)最低級(jí)的代碼復(fù)用?
答案當(dāng)然是——NO,所以,我們要自己實(shí)現(xiàn)繼承!
目標(biāo)
最關(guān)鍵的目標(biāo)當(dāng)然是繼承——子類自動(dòng)擁有父類的所有公共屬性和方法。
支持instanceof,例如c是子類的實(shí)例,而P是父類,c instanceof P應(yīng)該返回true。
其次應(yīng)該能夠重寫(Override)父類的方法,并且在子類的方法中,能夠方便地調(diào)用到父類的同名方法。
至于說重載(Overload),由于Javascript的語言特性(不可以有同名方法,即便它們參數(shù)列表不一樣),無法實(shí)現(xiàn)。
設(shè)計(jì)與實(shí)現(xiàn)
Javascript的對(duì)象有一個(gè)很重要的屬性——__proto__,也就是原型。原型實(shí)質(zhì)上也是一個(gè)對(duì)象,所有它也可以有自己的原型,這樣就形成一個(gè)原型鏈。當(dāng)你調(diào)用某個(gè)對(duì)象的某個(gè)方法,或者讀取該對(duì)象的某個(gè)屬性,Javascript執(zhí)行器是這樣做的:
1、首先到該對(duì)象中找對(duì)應(yīng)的方法或?qū)傩裕绻也坏剑?br />
2、到該對(duì)象的原型中找,如果還找不到,
3、到原型的原型里面找
4、...
5、直到最后找到Object的原型為止,如果還沒有則返回undefined
如下圖所示:
原型鏈的這個(gè)特性,和繼承很相似,所以自然而然,我們可以利用它來實(shí)現(xiàn)繼承機(jī)制。而原型鏈對(duì)instanceof的支持,使得它成為很好的選擇。
我們定義extend函數(shù),這個(gè)函數(shù)接受兩個(gè)參數(shù),第一個(gè)是父類,第二個(gè)是子類,如下所示:
function extend(ParentClass, ChildClass) { ... return ChildClass; }
這個(gè)函數(shù)對(duì)子類進(jìn)行處理,并返回子類。處理的邏輯如下:
建立原型鏈
通過將子類的原型鏈與父類的原型鏈連接起來,子類可以自動(dòng)擁有父類的方法和屬性:
var pp = ParentClass.prototype, cp = ChildClass.prototype; function T() {}; T.prototype = pp; ChildClass.prototype = new T();
為了連接原型鏈,需要?jiǎng)?chuàng)建一個(gè)父類的實(shí)例,并將其賦給子類的原型屬性。但我們不希望在extend方法里面就實(shí)例化父類,所以引入了一個(gè)中間類T,以解決這個(gè)問題。
實(shí)現(xiàn)重寫
原型鏈建立之后,原來子類原型上的方法和屬性我們也需要保留下來:
方法
如果父類有同名方法,我們使用一個(gè)閉包,來保留對(duì)父類方法和子類方法的引用。然后,修改新的原型中該方法的引用,將其指向一個(gè)新的 function。在這個(gè)function里面,我們創(chuàng)建一個(gè)臨時(shí)屬性super,將其指向父類方法,并調(diào)用子類方法,這樣在子類方法中,通過 this.super可以調(diào)用該父類方法:
ChildClass.prototype[name] = (function(pm, cm) { return function() { var _super = this.super; this.super = pm; var result = cm.apply(this, arguments); this.super = _super; return result; }; })(pp[name], cp[name]);
屬性
對(duì)于屬性,不存在重寫的問題,所以直接將子類原來的原型中的屬性加到新的原型中即可:
ChildClass.prototype[name] = cp[name];
構(gòu)造器
為了讓子類能夠訪問到父類的構(gòu)造器,我們將父類賦給子類的super屬性:
ChildClass.super = ParentClass;
如何使用
假設(shè)我們要設(shè)計(jì)一個(gè)管理系統(tǒng),里面涉及到客戶、工人和經(jīng)理等。將客戶和員工的共性抽象出來,我們得到人(People);然后將工人和經(jīng)理的共性抽象得到員工(Employee)。這樣我們得到三級(jí)類結(jié)構(gòu):
實(shí)現(xiàn)這個(gè)設(shè)計(jì)的代碼如下:
function People(firstname, lastname) { this.firstname = firstname; this.lastname = lastname; } function Employee(firstname, lastname, company) { Employee.super.apply(this, arguments); this.company = company; } function Manager(firstname, lastname, company, title) { Manager.super.apply(this, arguments); this.title = title; }
我們希望對(duì)每個(gè)人都有一個(gè)描述,People是姓+名;員工在姓+名之后,還包括公司名稱;而經(jīng)理在員工的描述之后,還包括職位。代碼如下:
People.prototype.summary = function() { return this.firstname + " " + this.lastname; }; Employee.prototype.summary = function() { return this.super.call(this) + ", " + this.company; }; Manager.prototype.summary = function() { return this.super.call(this) + ", " + this.title; };
在所有的成員方法都已經(jīng)定義好之后,聲明類的繼承(必須先定義方法,再聲明類的繼承,否則無法在方法中使用this.super調(diào)用父類方法?。?/p>
extend(People, Employee); extend(Employee, Manager);
使用這些類就比較簡單,直接new就好了:
var people = new People("Alice", "Dickens"); var employee = new Employee("Bob", "Ray", "Alibaba"); var manager = new Manager("Calvin", "Klein", "Alibaba", "Senior Manager"); console.log( people.summary() ); //Alice Dickens console.log( employee.summary() ); //Bob Ray, Alibaba console.log( manager.summary() ); //Calvin Klein, Alibaba, Senior Manager
這篇文章不錯(cuò)吧,那就給個(gè)贊吧!
- Javascript 繼承機(jī)制的實(shí)現(xiàn)
- Javascript 繼承機(jī)制實(shí)例
- javascript類繼承機(jī)制的原理分析
- Javascript繼承機(jī)制的設(shè)計(jì)思想分享
- 由JavaScript中call()方法引發(fā)的對(duì)面向?qū)ο罄^承機(jī)制call的思考
- 基于JavaScript實(shí)現(xiàn)繼承機(jī)制之構(gòu)造函數(shù)+原型鏈混合方式的使用詳解
- 基于JavaScript實(shí)現(xiàn)繼承機(jī)制之原型鏈(prototype chaining)的詳解
- 基于JavaScript實(shí)現(xiàn)繼承機(jī)制之調(diào)用call()與apply()的方法詳解
相關(guān)文章
javascript實(shí)現(xiàn)圖片自動(dòng)和可控的輪播切換特效
這篇文章主要介紹了javascript實(shí)現(xiàn)圖片自動(dòng)和可控的輪播切換特效,效果非常的棒,推薦給大家,有需要的小伙伴可以參考下。2015-04-04element-ui 圖片壓縮上傳功能實(shí)現(xiàn)
這篇文章主要介紹了element-ui 圖片壓縮上傳功能實(shí)現(xiàn),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-10-10localstorage實(shí)現(xiàn)帶過期時(shí)間的緩存功能
這篇文章主要介紹了localstorage實(shí)現(xiàn)帶過期時(shí)間的緩存功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-06-06JavaScript實(shí)現(xiàn)即時(shí)通訊的 4 種方案
這篇文章主要給大家分享了JavaScript實(shí)現(xiàn)即時(shí)通訊的 4 種方案,其實(shí)就是服務(wù)端如何將數(shù)據(jù)推送到瀏覽器,下面詳細(xì)的文章內(nèi)容,需要的小伙伴參考一下,洗碗給對(duì)你有所幫助2022-02-02怎么理解wx.navigateTo的events參數(shù)使用詳情
這篇文章主要介紹了怎么理解wx.navigateTo的events參數(shù)使用詳情,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05JS判斷空對(duì)象的幾個(gè)方法大盤點(diǎn)
在做數(shù)據(jù)交互的時(shí)候,我們經(jīng)常需要判斷數(shù)據(jù)或者對(duì)象是不是為空,避免當(dāng)接口異常時(shí)候前端頁面崩潰,下面這篇文章主要給大家介紹了關(guān)于JS判斷空對(duì)象的幾個(gè)方法,需要的朋友可以參考下2022-02-02