var Sub = function (color,list) { this.color = color; this.list = list } var sub1 = new Sub("red",[1]); var sub2 = new Sub("green",[2]); sub1.color = "new"; alert(sub1.color);//"new" alert(sub2.color);//"green",說明了sub2.color不會被sub1影響 sub1.list.push(4); alert(sub1.list);//[1,4] alert(sub2.list);//[2],說明引用類型array仍然不會被sub1影響
能夠看出構造函數自身的屬性(不管直接類型仍是引用類型),都是賦值一份copy給它的全部instance,所以每個instance 的修改互相不影響。咱們繼續看:javascript
var Super = function(color,list) { this.color = color; this.list = list } Super.prototype.newList = [10,10,10,10]; //建立Super的instance:①super1;②咱們能夠把Sub.prototype當作是Super的instance。 var super1 = new Super("red",[1,1,1,1]); var Sub = function () {}; Sub.prototype = new Super("green",[1]);//到這裏咱們能夠當作 Sub.prototype和super1都是Super的instance var sub1 = new Sub(); var sub2 = new Sub(); //修改Super的`自身屬性` Sub.prototype.list.push(4);//不影響兄弟(super1),但會影響自身的instance(sub1,sub2) alert(sub1.list);//[1,4] alert(sub2.list);//[1,4] alert(super1.list);//[1,1,1,1] 構造函數`自身屬性`是直接賦值給它的全部instance,也就是說Super自己的屬性(color,list,無論是直接類型仍是引用類型)都是分別複製一份給super1和Sub.prototype,所以修改了Sub.prototype的屬性(list)並不會影響到super1。反之亦然。 //修改Super的`prototype屬性` Sub.prototype.newList.push(2,2,2,2); sub1.newList;//[10,10,10,10,2,2,2,2] super1.newList;//[10,10,10,10,2,2,2,2];構造函數的prototype裏的屬性只是提供一個指針給全部的instance,所以修改了Sub.prototype的屬性(newList:引用類型; 直接類型是沒法修改的,只能覆寫)會影響到super1。修改Sub.prototype至關因而直接修改Super.prototype屬性,由於它們經過原型鏈引用着同一個屬性。
咱們在來理清關於構造函數自身屬性
和prototype屬性
與instance之間的關係,先看圖:
咱們來總結一下:java
var Super = function(color,list) { this.color = color; this.list = list } Super.prototype.newList = [10,10,10,10]; //建立Super的instance:①super1;②咱們能夠把Sub.prototype當作是Super的instance。 var super1 = new Super("red",[1,1,1,1]); var Sub = function () {}; Sub.prototype = new Super("green",[1]);//到這裏咱們能夠當作 Sub.prototype和super1都是Super的instance var sub1 = new Sub(); var sub2 = new Sub(); //修改Super的`自身屬性` Sub.prototype.list.push(4);//不影響兄弟(super1),但會影響自身的instance(sub1,sub2),由於對於sub1,sub2來講list是原型屬性而不是自身屬性了,這裏理解起來可能有點亂。 alert(sub1.list);//[1,4] alert(sub2.list);//[1,4] alert(super1.list);//[1,1,1,1] 構造函數`自身屬性`是直接賦值給它的全部instance,也就是說Super自己的屬性(color,list,無論是直接類型仍是引用類型)都是分別複製一份給super1和Sub.prototype,所以修改了Sub.prototype的屬性(list)並不會影響到super1。反之亦然。 //覆寫 Sub.prototype.list = [0]; sub1.list;//[0] sub2.list;//[0] super1.list;//[1,1,1,1]
sub1.newList.push(2,2,2,2) ==> Sub.prototype.newList.push(2,2,2,2) ==> Super.prototype.newList.push(2,2,2,2)
,也就是sub1會沿着原型鏈一直查找到最終的Super.prototype,在Super.prototype裏去修改newList屬性,所以原型鏈上全部引用了改屬性的實例都會被影響。
var Super = function(color,list) { this.color = color; this.list = list } Super.prototype.newList = [10,10,10,10];//建立Super的instance:①super1;②咱們能夠把Sub.prototype當作是Super的instance。 var super1 = new Super("red",[1,1,1,1]); var Sub = function () {}; Sub.prototype = new Super("green",[1]);//到這裏咱們能夠當作 Sub.prototype和super1都是Super的instance var sub1 = new Sub(); var sub2 = new Sub(); //修改Super的<code>prototype屬性</code> Sub.prototype.newList.push(2,2,2,2); sub1.newList;//[10,10,10,10,2,2,2,2] super1.newList;//[10,10,10,10,2,2,2,2];構造函數的prototype裏的屬性只是提供一個指針給全部的instance,所以修改了Sub.prototype的屬性(newList:引用類型)會影響到super1。修改Sub.prototype至關因而直接修改Super.prototype屬性,由於它們經過原型鏈引用着同一個屬性。
var Super = function(color,list) { this.color = color; this.list = list } Super.prototype.newList = [10,10,10,10]; //建立Super的instance:①super1;②咱們能夠把Sub.prototype當作是Super的instance。 var super1 = new Super("red",[1,1,1,1]); var Sub = function () {}; Sub.prototype = new Super("green",[1]);//到這裏咱們能夠當作 Sub.prototype和super1都是Super的instance var sub1 = new Sub(); var sub2 = new Sub(); sub1.newList = [2,2,2,2];//覆寫 sub1.newList;//[2,2,2,2] sub2.newList;//[10,10,10,10] super1.newList;//[10,10,10,10] sub2和super1不受影響,實例覆寫方法只會在sub1自身的做用域裏添加此方法,而不會修改到Super.prototype的方法
因爲是覆寫而不是修改,所以不會沿着原型鏈查找,而是在當前的做用域裏添加該屬性,而原來原型鏈上的那個屬性依然還在,不受影響。這就實現了方法的重載。segmentfault
2.父函數方法覆寫:假如是在Super.prototype裏對newList進行覆寫,那麼全部引用該屬性的實例都將被影響。函數
var Super = function(color,list) { this.color = color; this.list = list } Super.prototype.newList = [10,10,10,10]; //建立Super的instance:①super1;②咱們能夠把Sub.prototype當作是Super的instance。 var super1 = new Super("red",[1,1,1,1]); var Sub = function () {}; Sub.prototype = new Super("green",[1]);//到這裏咱們能夠當作 Sub.prototype和super1都是Super的instance var sub1 = new Sub(); var sub2 = new Sub(); Super.prototype.newList = [2,2,2,2]; sub1.newList;//[2,2,2,2] sub2.newList;//[2,2,2,2] super1.newList;//[2,2,2,2]
3.徹底對prototype覆寫:當咱們使用XXX.prototype = YYY;
對XXX.prototype進行徹底覆寫時,會完全改變原型鏈。可是咱們應該注意一點,覆寫前的instance依然保持着對原有prototype的引用,所以原有的prototype中的屬性不會被GC,依然保存在內存中,徹底覆寫後咱們依然能夠訪問原來的instance所引用的屬性和方法;而新建立的instance會指向新的prototype,所以沒法再訪問覆寫前prototype中的屬性。this
var Super = function(color,list) { this.color = color; this.list = list } Super.prototype.newList = [10,10,10,10]; new Super; var Sub = function () {}; Sub.prototype.sayHello = function(){return "hello"}; var sub1 = new Sub();//徹底覆寫Sub.prototype前建立的instance Sub.prototype = new Super("green",[1]);//這裏能夠理解爲對Sub的prototype進行徹底覆寫,所以會從新建立一個新的prototype指向Super.prototype var sub2 = new Sub();//徹底覆寫Sub.prototype後建立的instance //驗證 sub1.__proto__.constructor == Sub;//true。依然引用着原來的原型鏈 sub2.__proto__.constructor == Super;//true。新的instance引用了新的原型鏈 sub1.sayHello();//"hello",依然能訪問原有的屬性,說明還保存在內存中。 sub2.sayHello();//"對象不支持sayHello",新的實例沒法再引用原有的prototype
到這裏整個關係就理清了,相信仍是不少人看不懂,但假如你真的但願學好javascript,這篇文章足夠你讀10遍,理清原型關係將是你可否跨上另外一個臺階的關卡,不理清這一層關係的話,後患無窮。我畫了一張神同樣的圖,看得懂的話基本就能理清原型繼承關係了。
ps: 圖說明的是覆寫的過程,請區分覆寫和修改的區別。(Sub.prototype.list.push(1)是修改,Sub.prototype.list = [1]是覆寫)spa