1、原型繼承父類的實例app
//父類及其原型屬性/方法 function SuperType () { this.name = ['zc','ls','ww']; } SuperType.prototype.getSuperName = function() { return this.name; }; //子類及其原型屬性/方法 function SubType() { this.test = ['a','b','c','d']; } //子類型的原型指向父類型的實例(即子類的原型複製了父類的構造器以及父類原型屬性/方法) SubType.prototype = new SuperType();① //爲子類原型添加原型拓展屬性/方法 SubType.prototype.getSubTest = function() { return this.test; } var instance1 = new SubType(); instance1.name.push('yzy');//name屬性是原型繼承自父類實例 instance1.test.push('e');//test屬性是源於子類自己的構造器 console.log(instance1.name,instance1.test) var instance2 = new SubType(); console.log(instance2.name,instance2.test)
控制檯輸出:函數
標註:測試
①注意這裏的子類原型指向一個父類的實例(引用傳遞),那麼這塊的父類實例就是內存中的一塊地址,之後全部的子類實例都會有一個原型屬性指向這塊地址,而且子類A對這塊地址中數據更改也會影響到子類B。this
圖示:spa
因此你能夠看到,instance1.name是從父類實例來的,這個屬性實際存在於這個單例,訪問的時候都是引用傳遞,因爲這個單例是共享的,instance1 push了一個數據,那麼就算instance2沒有任何動做,instance2讀的時候數據也會是變化後的數據;prototype
而對於test屬性,是子類自身的,因此這個屬性值存在於子類實例自身,相互之間互不影響,因此雖然instance1.test push了一個數據,可是instance2訪問的時候絲絕不受影響。3d
缺點:繼承自父類實例的原型屬性會被全部實例所共享。code
2、構造函數假裝(call()、apply())對象
//父類及其原型屬性/方法 function SuperType(name) { this.name = name; this.color = ['green','red']; } SuperType.prototype.getSuperName = function() { return this.name; } //子類及其原型屬性/方法 function SubType(name) { SuperType.call(this, name);① this.test = ['a','b','c','d']; } SubType.prototype.getSubTest = function() { return this.test; } var instance1 = new SubType('Jack'); console.log(instance1.name,instance1.getSubTest()); console.log('------------------------'); console.log(instance1.getSuperName())
控制檯輸出:blog
標註:
①call()方法實際上就是在當前做用域拷貝了一下函數執行者的構造函數/方法,因此上述call()方法實際上作了以下的事情
//子類及其原型屬性/方法 function SubType(name) { //SuperType.call(this, name); this.name = name; this.color = ['green','red']; this.test = ['a','b','c','d']; }
注意的是,call()函數假裝並不會在當前做用域執行 SuperType 原型下的方法/屬性
因此,由於 getSuperName() 是父類原型下的方法,因此call() 方法天然不會複製該方法給 SubType 構造器,所以控制檯報錯也就是理所固然的咯
缺點:函數假裝不會繼承父類原型下的屬性/方法。
3、組合繼承(函數假裝 + 原型繼承)
//父類及其原型屬性/方法 function SuperType(name) { this.name = name; } SuperType.prototype.getSuperName = function () { return this.name; } // 子類1及其原型屬性/方法 function SubType1(name) { SuperType.call(this, name); this.test = ['h1', 'h2', 'h3', 'h4']; } SubType1.prototype = SuperType.prototype;① SubType1.prototype.getSubTest = function () { return this.test; } // 子類2及其原型屬性/方法 function SubType2(name) { SuperType.call(this, name); this.age = 18; } SubType2.prototype = SuperType.prototype; SubType2.prototype.getSubAge = function () { return this.age; } var instance1 = new SubType1('Jack'); var instance2 = new SubType2('Tom'); console.log(instance1,instance2);
控制檯輸出:
標註:
①這裏子類原型繼承自父類原型,而後子類爲原型添加了原型拓展,這裏的原型繼承是引用傳遞,因此添加拓展的操做都是基於同一塊內存地址的。
圖示:
因此,不管是父類的原型屬性仍是子類繼承的原型(父類原型),實際上都是引用傳遞,都指向內存中的同一塊地址,所以,上述的代碼,雖然子類2雖然沒有原型方法 getSubTest,可是實際上子類1已經在他們指向的共同內存地址添加了該方法,同理子類1也是。
缺點:子類型的原型屬性共享。
4、寄生組合式繼承
function object(o) { function F() { }; F.prototype = o; return new F(); } //寄生組合式繼承 ① function inheritPrototype(subType, superType) { var prototype = object(superType.prototype); subType.prototype = prototype; } //父類及其原型屬性/方法 function SuperType(name) { this.name = name; } SuperType.prototype.getSuerperName = function () { return this.name; } //子類1及其原型屬性/方法 function SubType(name) { SuperType.call(this, name); this.test = ['h1', 'h2', 'h3', 'h4']; } inheritPrototype(SubType, SuperType); SubType.prototype.getSubTest = function () { return this.test; }; //子類2及其原型屬性/方法 function SubType2(name) { SuperType.call(this, name); this.test2 = ['s1', 's2', 's3', 's4']; } inheritPrototype(SubType2, SuperType); SubType2.prototype.getSubTest2 = function () { return this.test2; }; /* 如下爲測試代碼示例 */ var instance1 = new SubType(['yyy', 'Jack', 'Nick']); var instance2 = new SubType2(['yyy2', 'Jack2', 'Nick2']); console.log(instance1,instance2)
控制檯輸出:
標註:
①咱們看這個寄生組合式繼承的處理方式,傳進來一個子類和父類,子類的原型 = 新對象(新對象的原型 = 父類的原型),因此就是子類原型下的原型 = 父類的原型
這就是咱們所看到的上面控制檯輸出的結果了,父類的原型掛在子類原型下的原型下,這樣爲各個子類添加原型的時候就不會影響掛在上面的父類原型了。
可是,因爲依舊是引用傳遞,因此這個子類原型下原型(繼承自父類的原型)依舊是共享的。
圖示:
爲達上述目的,我這邊直接將父類實例掛在子類原型上,也是能夠的:
//寄生組合式繼承 function inheritPrototype(subType, superType) { subType.prototype =new superType();② } //父類及其原型屬性/方法 function SuperType(name) { if(name){ this.name = name; } } SuperType.prototype.getSuerperName = function () { return this.name; } //子類1及其原型屬性/方法 function SubType(name) { SuperType.call(this, name); this.test = ['h1', 'h2', 'h3', 'h4']; } inheritPrototype(SubType, SuperType); SubType.prototype.getSubTest = function () { return this.test; }; //子類2及其原型屬性/方法 function SubType2(name) { SuperType.call(this, name); this.test2 = ['s1', 's2', 's3', 's4']; } inheritPrototype(SubType2, SuperType); SubType2.prototype.getSubTest2 = function () { return this.test2; }; /* 如下爲測試代碼示例 */ var instance1 = new SubType(['yyy', 'Jack', 'Nick']); var instance2 = new SubType2(['yyy2', 'Jack2', 'Nick2']); console.log(instance1,instance2)
標註:
②這裏掛載在子類原型下的原型的是一個父類的實例,值得注意的是,實例化一個父類實例是會自動調用父類構造器的,因此會將父類構造器以及父類原型一同掛載到子類原型下的原型下,不妨讓咱們把上述例子中的父類構造器if判斷去掉看看控制檯輸出結果:
講到這裏你是否是以爲已經結束了???固然~~~沒有!
上面說過:這個子類原型下原型(繼承自父類的原型)依舊是共享的!
那麼我後來作了個實驗:
Ⅰ.父類原型屬性值是基本數據類型:
//寄生組合式繼承 function inheritPrototype(subType, superType) { subType.prototype =new superType(); } //父類及其原型屬性/方法 function SuperType(name) { if(name){ this.name = name; } } SuperType.prototype.getSuerperName = function () { return this.name; }
SuperType.prototype.age = 12 SuperType.prototype.console = function(){ this.age += 1; console.log(this.age) };
//子類1及其原型屬性/方法 function SubType(name) { SuperType.call(this, name); this.test = ['h1', 'h2', 'h3', 'h4']; } inheritPrototype(SubType, SuperType); SubType.prototype.getSubTest = function () { return this.test; }; //子類2及其原型屬性/方法 function SubType2(name) { SuperType.call(this, name); this.test2 = ['s1', 's2', 's3', 's4']; } inheritPrototype(SubType2, SuperType); SubType2.prototype.getSubTest2 = function () { return this.test2; }; /* 如下爲測試代碼示例 */ var instance1 = new SubType(['yyy', 'Jack', 'Nick']); var instance2 = new SubType2(['yyy2', 'Jack2', 'Nick2']); instance1.console(); instance1.console(); instance1.console(); instance1.console(); instance1.console(); instance2.console();
控制檯輸出:
結果說明:父類原型下的age屬性沒有共享!
Ⅱ.父類原型屬性值是非基本數據類型(例如:對象):
//寄生組合式繼承 function inheritPrototype(subType, superType) { subType.prototype =new superType(); } //父類及其原型屬性/方法 function SuperType(name) { if(name){ this.name = name; } } SuperType.prototype.getSuerperName = function () { return this.name; }
SuperType.prototype.age = { age:12 } SuperType.prototype.console = function(){ this.age.age += 1; console.log(this.age.age) };
//子類1及其原型屬性/方法 function SubType(name) { SuperType.call(this, name); this.test = ['h1', 'h2', 'h3', 'h4']; } inheritPrototype(SubType, SuperType); SubType.prototype.getSubTest = function () { return this.test; }; //子類2及其原型屬性/方法 function SubType2(name) { SuperType.call(this, name); this.test2 = ['s1', 's2', 's3', 's4']; } inheritPrototype(SubType2, SuperType); SubType2.prototype.getSubTest2 = function () { return this.test2; }; /* 如下爲測試代碼示例 */ var instance1 = new SubType(['yyy', 'Jack', 'Nick']); var instance2 = new SubType2(['yyy2', 'Jack2', 'Nick2']); instance1.console(); instance1.console(); instance1.console(); instance1.console(); instance1.console(); instance2.console();
控制檯輸出:
結果說明:父類原型下的age屬性共享!
綜上所述:
原型上的基本數據類型屬性是值傳遞(內存地址不共享);
原型上的非基本數據類型屬性是引用傳遞(內存地址共享)。