摘要:組合繼承組合繼承有時(shí)也叫偽經(jīng)典繼承,該繼承模式將原型鏈和借用構(gòu)造函數(shù)的技術(shù)結(jié)合在一起實(shí)現(xiàn)。寄生組合式繼承通過借用構(gòu)造函數(shù)來繼承屬性,通過原型鏈的混成形式來繼承方法。
原文地址:JavaScript實(shí)現(xiàn)繼承
眾所周知,JavaScript 這門語言在 ES6 出來之前是沒有類(class)這一概念的,所以 JavaScript 中的類都是通過原型鏈來實(shí)現(xiàn)的。同樣,使用 JavaScript 也能實(shí)現(xiàn)面向?qū)ο蟮膶?shí)現(xiàn)繼承。以下是《高程》(第三版)的讀書筆記。
原型鏈通過原型鏈實(shí)現(xiàn)繼承很容易理解,也很簡(jiǎn)單。將子類的原型指向父類的實(shí)例即可。寫個(gè)簡(jiǎn)單的例子:
// 父類 function SuperType() { this.property = "super"; } // 在父類的原型上定義方法 SuperType.prototype.getSuperVal = function () { console.log(this.property); }; // 子類 function SubType() { this.property = "sub"; } // 子類繼承父類 SubType.prototype = new SuperType(); var instance = new SubType(); console.log(instance.getSuperVal()); // "sub"
對(duì)于子類來講,其原型的指向應(yīng)該是這樣的:SubType -> new SuperType() -> SuperType.prototype -> Object.prototype -> null。
注意:
如果想要給子類添加原型上的方法,需要在子類繼承了父類后添加,否則會(huì)被父類實(shí)例所覆蓋。
也不要用對(duì)象字面量的方式給子類原型添加新方法,這會(huì)使得之前的繼承失效。
原型鏈的問題:
父類的實(shí)例屬性成為了子類的原型屬性,即子類的實(shí)例共享了該父類實(shí)例的屬性,如果該屬性是引用類型,則子類的實(shí)例對(duì)該屬性的修改會(huì)反映在所有的子類實(shí)例上。
在創(chuàng)建子類實(shí)例時(shí),不能向父類的構(gòu)造函數(shù)傳遞參數(shù)。實(shí)際上,應(yīng)該說是沒有辦法在不影響所有對(duì)象實(shí)例的情況下,給父類的構(gòu)造函數(shù)傳遞參數(shù)。
借用構(gòu)造函數(shù)這個(gè)方法是為了解決原型鏈方式帶來的問題,使用十分巧妙,利用了 call 方法。代碼實(shí)現(xiàn):
// 父類 function SuperType() { this.users = ["Jack", "Tom"]; } // 子類 function SubType() { // 繼承 SuperType.call(this); } var instance1 = new SubType(); var instance2 = new SubType(); instance1.users.pop(); // "Tom" console.log(instance2.users); // ["Jack", "Tom"]
通過借用構(gòu)造函數(shù)解決了共享原型屬性導(dǎo)致的問題。同時(shí)也可以通過 call 方法給父類傳遞參數(shù)。
借用構(gòu)造函數(shù)的問題:
方法都需要在構(gòu)造函數(shù)(父類或子類)中定義,無法達(dá)到函數(shù)復(fù)用的功能。
組合繼承組合繼承有時(shí)也叫偽經(jīng)典繼承,該繼承模式將原型鏈和借用構(gòu)造函數(shù)的技術(shù)結(jié)合在一起實(shí)現(xiàn)。示例代碼:
// 父類 function SuperType(company) { this.company = company; this.staffs = ["Jack", "Tom"]; } // 父類方法 SuperType.prototype.getCompany = function () { console.log(this.company); }; // 子類 function SubType(company, product) { // 繼承屬性 SuperType.call(this, company); this.product = product; } // 繼承方法 SubType.prototype = new SuperType(); // 指向正確的constructor SubType.prototype.constructor = SubType; SubType.prototype.getProduct = function () { console.log(this.product); }; // SubType實(shí)例 var instance1 = new SubType("A", "tellphone"); instance1.getCompany(); // "A" instance1.getProduct(); // "tellphone" instance1.staffs.push("Amy"); // ["Jack", "Tom", "Amy"] var instance2 = new SubType("B", "toy"); instance2.getCompany(); // "B" instance2.getProduct(); // "toy" console.log(instance2.staffs); // ["Jack", "Tom"]
從代碼的例子可以觀察到,組合繼承模式可以讓子類的多個(gè)實(shí)例既能擁有自己的屬性,又能使用相同的方法,融合了原型鏈和借用構(gòu)造函數(shù)的優(yōu)點(diǎn)。
原型式繼承原型式繼承是借助原型可以基于已有的對(duì)象創(chuàng)建新對(duì)象,同時(shí)還不必因此創(chuàng)建自定義類型。使用如下函數(shù)實(shí)現(xiàn):
function object(obj) { function F() {} F.prototype = obj; return new F(); }
object 函數(shù)對(duì)傳入的對(duì)象實(shí)現(xiàn)了淺復(fù)制。所以對(duì)所有由其創(chuàng)建的實(shí)例都共享了 obj 對(duì)象中的引用屬性。
寄生式繼承寄生式繼承模式和原型式繼承模式很相似,創(chuàng)建了一個(gè)僅用于封裝繼承過程的函數(shù),在函數(shù)內(nèi)部增強(qiáng)對(duì)象的功能:
function createAnother(obj) { var clone = object(obj); clone.saySomething = function () { alert("Hello world!"); }; return clone; } function object(obj) { function F() {} F.prototype = obj; return new F(); }
通過 createAnother 函數(shù),對(duì)對(duì)象的功能進(jìn)行增強(qiáng),然而這種方式也沒有達(dá)到函數(shù)復(fù)用的效果,這一點(diǎn)和構(gòu)造函數(shù)模式一樣。
寄生組合式繼承通過借用構(gòu)造函數(shù)來繼承屬性,通過原型鏈的混成形式來繼承方法。寄生組合模式使用寄生模式來實(shí)現(xiàn)對(duì)父類原型的繼承,再將結(jié)果指定給子類的原型。其基本模式如下:
function inheritPrototype(subType, superType) { // 返回父類原型副本并賦值給子類原型 subType.prototype = object(superType.prototype); subType.prototype.constructor = subType; }
再來看一個(gè)例子:
// 父類 function SuperType(name) { this.name = name; } // 在父類的原型上定義方法 SuperType.prototype.getName = function () { console.log(this.name); }; // 子類 function SubType(name, age) { SuperType.call(this, name); this.age = age; } // 寄生組合繼承 inheritPrototype(SubType, SuperType); // 添加子類方法 SubType.prototype.getAge = function () { console.log(this.age); };
和組合繼承模式相比,寄生組合式繼承模式只調(diào)用了一次 SuperType 構(gòu)造函數(shù),也避免了在 SubType.prototype 上創(chuàng)建多余的屬性。開發(fā)人員普遍認(rèn)為寄生組合式繼承是引用類型最理想的繼承范式。
參考書籍《JavaScript高級(jí)程序設(shè)計(jì)》(第三版)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/85114.html
摘要:首先,需要來理清一些基礎(chǔ)的計(jì)算機(jī)編程概念編程哲學(xué)與設(shè)計(jì)模式計(jì)算機(jī)編程理念源自于對(duì)現(xiàn)實(shí)抽象的哲學(xué)思考,面向?qū)ο缶幊淌瞧湟环N思維方式,與它并駕齊驅(qū)的是另外兩種思路過程式和函數(shù)式編程。 JavaScript 中的原型機(jī)制一直以來都被眾多開發(fā)者(包括本人)低估甚至忽視了,這是因?yàn)榻^大多數(shù)人沒有想要深刻理解這個(gè)機(jī)制的內(nèi)涵,以及越來越多的開發(fā)者缺乏計(jì)算機(jī)編程相關(guān)的基礎(chǔ)知識(shí)。對(duì)于這樣的開發(fā)者來說 J...
摘要:使用構(gòu)造函數(shù)的原型繼承相比使用原型的原型繼承更加復(fù)雜,我們先看看使用原型的原型繼承上面的代碼很容易理解。相反的,使用構(gòu)造函數(shù)的原型繼承像下面這樣當(dāng)然,構(gòu)造函數(shù)的方式更簡(jiǎn)單。 五天之前我寫了一個(gè)關(guān)于ES6標(biāo)準(zhǔn)中Class的文章。在里面我介紹了如何用現(xiàn)有的Javascript來模擬類并且介紹了ES6中類的用法,其實(shí)它只是一個(gè)語法糖。感謝Om Shakar以及Javascript Room中...
摘要:如下代碼所示,可以使用構(gòu)造函數(shù)來創(chuàng)建父對(duì)象,這樣做的話,自身的屬性和構(gòu)造函數(shù)的原型的屬性都將被繼承。方法繼承自對(duì)象這是中構(gòu)造函數(shù)鏈的一個(gè)示例。 代碼復(fù)用及其原則 代碼復(fù)用,顧名思義就是對(duì)曾經(jīng)編寫過的代碼的一部分甚至全部重新加以利用,從而構(gòu)建新的程序。在談及代碼復(fù)用的時(shí)候,我們首先可以想到的是繼承性。代碼復(fù)用的原則是: 優(yōu)先使用對(duì)象組合,而不是類繼承 在js中,由于沒有類的概念,因此實(shí)例...
摘要:組合繼承前面兩種模式的特點(diǎn)類式繼承通過子類的原型對(duì)父類實(shí)例化實(shí)現(xiàn)的,構(gòu)造函數(shù)式繼承是通過在子類的構(gòu)造函數(shù)作用環(huán)境中執(zhí)行一次父類的構(gòu)造函數(shù)來實(shí)現(xiàn)的。 類有三部分 構(gòu)造函數(shù)內(nèi)的,供實(shí)例化對(duì)象復(fù)制用的 構(gòu)造函數(shù)外的,直接通過點(diǎn)語法添加的,供類使用,實(shí)例化對(duì)象訪問不到 類的原型中的,實(shí)例化對(duì)象可以通過其原型鏈間接地訪問到,也是供所有實(shí)例化對(duì)象所共用的。 類式繼承 類的原型對(duì)象的作用就是為類...
摘要:抽象類是一種聲明但是不能使用的類,當(dāng)使用時(shí)就會(huì)報(bào)錯(cuò)。但是是靈活的,可以在類的方法中手動(dòng)地拋出錯(cuò)誤來模擬抽象類。 抽象類是一種聲明但是不能使用的類,當(dāng)使用時(shí)就會(huì)報(bào)錯(cuò)。在JavaScript中,abstract還是一個(gè)保留字,不能像傳統(tǒng)的面向?qū)ο笳Z言那樣輕松創(chuàng)建。但是JavaScript是靈活的,可以在類的方法中手動(dòng)地拋出錯(cuò)誤來模擬抽象類。如下: var Car = function(){...
閱讀 2042·2023-04-26 01:33
閱讀 1669·2023-04-26 00:52
閱讀 1052·2021-11-18 13:14
閱讀 5466·2021-09-26 10:18
閱讀 2919·2021-09-22 15:52
閱讀 1498·2019-08-29 17:15
閱讀 3028·2019-08-29 16:11
閱讀 1046·2019-08-29 16:11