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