javascript—自身屬性與原型屬性關係

最基本的構造函數與實例的關係:


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


自身屬性和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(); //修改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

  1. Sub.prototype 和 super1 都是經過 new Super()產生的,咱們把它們兩個叫作兄弟;同理sub1和sub2也是兄弟
  2. 自身屬性(圖中a,B):不管是直接類型仍是引用類型,兄弟間互不影響,各自擁有一份父函數的copy。修改只是在自身做用域裏修改。好比說③和⑤都是①的實例,都擁有①中全部屬性的copy,修改③的屬性至關於在③中修改,不會影響其餘人。
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]
  • prototype屬性(圖中XXXXX):* 修改:好比說經過⑥去修改①的prototype的屬性——sub1.newList.push(2,2,2,2),那麼由於原型屬性是引用而非複製,所以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屬性,由於它們經過原型鏈引用着同一個屬性。
  • 覆寫:假如咱們不是修改屬性,而是直接覆寫屬性,那麼狀況也會不同:
    1.instance方法重載:

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

相關文章
相關標籤/搜索