在es6中已經有語法糖extends去實現類的繼承,然而多繼承是不被容許也是不被提倡的,由於會致使‘鑽石問題’:好比說b和c都繼承自a,d繼承自b和c,那麼d裏面就會有同一個方法來自於兩個兩個祖先,那麼當在d中調用這個方法時就會出現邏輯問題,究竟是調用b的呢仍是c的呢?java就是看到了c++多繼承的問題因此就乾脆不容許多繼承了,js由於自身的靈活性,咱們依然能夠經過一些方式來實現多繼承(不少地方叫作mixin,實際上聽起來更正確)。javascript
var A = function(name) { this.name1 = name; // this.name表明函數名,這裏避免衝突 } var B = function(age) { this.age = age; this.getAge = function() {} } var C = function(name, age) { A.call(this, name); B.call(this, age); } // usage var c = new C('n', 2);複製代碼
以上就是利用call或者apply來‘繼承’父類屬性及方法,其實本質上就是調用方法利用this把屬性組合到一塊兒。由於調用A方法是在上下文中加了一個name屬性,調用B就是加了age屬性,而你在new C的時候,js創造了一個對象,而且把上下文指向了這個對象,因此AB的屬性也就天然添加到了這個對象裏。html
因此下面用這個思路寫一個extend方法:(extends是關鍵字,直接用這名字會報錯)java
function extend(...args) { for (var i = 0; i < args.length; i++) { args[i].call(this) } } // usage var s = new extend(Class1, Class2);複製代碼
固然你能夠看到,用這個方法的話你是不能傳參的c++
上面調用函數的方法模擬多繼承當然能夠知足咱們繼承祖先的屬性方法這一要求,但是這個最後的孩子其實不是真正的後代,由於不難看出他和前面的class不在同一條原型鏈上,因此用c instanceof A
返回false。並且一個缺點就是那個類的方法必須像this.getAge()這樣定義在類裏面,若是你new了多個實例就會重複創造這個方法,形成內存的浪費。es6
爲了省內存,必然會想到往prototype添加方法的方式,然而經過上面的調用顯然不能期望這些方法也出如今this上,因此咱們要經過new來複制這些方法算法
var A = function() { this.name1 = ''; } var B = function() { this.age = ''; } B.prototype.getAge = function() {} var extend = function(...args) { var ClassC = function() {} for (var i = 0; i < args.length; i++) { var base = new args[i](); for (var f in base) { ClassC.prototype[f] = base[f]; } } return ClassC; } // usage var C = extend(A, B); var c = new C();複製代碼
for...in會把全部原型鏈上的可枚舉屬性都列出來,因此拿到那些類的實例後,再把這些屬性拷貝到咱們本身類的原型上就行。bash
這個依然不能傳參,不能instanceofmarkdown
function A(name) { this.name1 = name; } A.prototype.getName = function() {} function B(age) { weight = 100; // 私有屬性,體重 this.age = age; } B.prototype.getAge = function() {} B.prototype.getWeight = function() {return weight;} B.prototype.addWeight = function() {weight++;} function extend() { for (var i = 0; i < arguments.length; i++) { var tempBase = new arguments[i](); for (var prop in tempBase) { if (!tempBase.hasOwnProperty(prop)) { // 只放鏈上的屬性 this.prototype[prop] = tempBase[prop]; } } } } // usage function Child(name, age) { A.call(this, name); // 傳參,以及添加自己的屬性 B.call(this, age); } extend.call(Child, A, B); var child = new Child('lol', 5);複製代碼
以上把前面代碼改了兩點,1是前面直接定義一個class在extend裏面,因此直接寫死了Class.protorype[prop] = ..., 這裏寫成this.prototype由於後面在調用extend方法時會把上下文指定成你的class,而且經過了hasOwnProperty去檢查這個屬性是否是本身的。app
2後面你就能夠定義本身的構造器了,並實現的傳參的須要。函數
這裏稍微提一下私有屬性,我看了一些博客後發現你們對私有屬性理解好像有問題。其實像上面的weight直接寫在裏面就是私有的,由於私有的定義是這個function裏面的其餘函數能用之外,其餘人都不行。好比私有屬性/方法是沒法繼承的;這個類的實例也沒法直接訪問到的,要訪問/修改必須你本身暴露出getter/setter,就好比上面的getWeight方法,若是你在Child類裏面添加了weight的話就會返回它,不然返回父類的那個私有變量。
上面全部的都只是僞裝繼承,用instanceof都得不到你想要的結果不能用來判斷某對象是否是某類的實例或子類,爲了實現這一效果,你要寫本身的isInstanceOf()方法,思路是在每一個子類裏面存一個全部父類的引用而後作對比,另外爲了解決多繼承的鑽石問題要用到MRO算法。下面博客寫了這個,然而也不完美,因此各看官本身斟酌。
引用: