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

相关文章
相关标签/搜索