ECMAScriptjavascript 原型链

浅谈JS原型链

原型链

ECMAScript中讲述了原型链的定义。咱们知道ECMAScript并不像C++,Java这样采纳类,但是对象依旧能够透过多种办法创立,其中就有构造函数模式。每个构造函数都有一个原型对象,同时都有一个prototype属性,
prototype属性指向构造函数的原型对象,它被用来促成基于原型的继承和共享。而原型对象又都默认会取得一个constructor属性,那么些特性包含一个针对构造函数(prototype属性所在函数)的指针。每个通过调用构造函数创设的实例对象都怀有一个针对性原型对象的指针,ECMA-262第5版中叫这些指针为[[prototype]],即便在剧本上从不正式的方法访问[[prototype]],但Chrome、Firefox和Safari在每个对象上都帮忙一个属性_proto_,而在其余实现中,这么些特性对脚本是截然不可见的。即便原型对象等于另一个项目标实例,那么它就有所指向创设该实例的构造函数的原型对象的指针,依此类推,就形成了一条指针链,这就是原型链的定义。通过下边的图形我们得以更清晰地询问原型链的概念。

ECMA5中可以运用Object.getPrototypeOf()来取得实例的构造函数的prototype

ECMAScript 1

实则,上图所出示的原型链还少一环。大家领悟,所有引用类型默认都卫冕了Object,而以此连续也是通过原型链实现的。函数是可调用的对象,所有函数的默认原型对象都是Object的实例,所以函数的原型对象都会蕴藏一个指向Object构造函数的原型对象的指针,也即指向Object.prototype的指针[[prototype]]。这样就分解了为啥所有自定义对象类型都会延续toLocaleString()、toString()等Object原型对象的默认方法了。仍旧来上图吧。

ECMAScript 2

理所当然,还有很关键的少数是大家需要小心的:对象实例中的指针[[prototype]]只指向原型对象,并不指向构造函数。

原型语法

日常,我们得以用一个蕴含所有属性和方法的对象字面量来重写整个原型对象。例如

1 function Person(){}
2 Person.prototype = {
3   name: "bella",
4   age: 21,
5   sayHello: function(){
6     alert(this.name);
7   }
8 }

不过,大家需要注意的是,重写之后,构造函数Person的原型对象的constructor属性不再指向Person了,因为该语法的面目是全然重写了默认的原型对象,所以constructor属性也就成为了新对象的constructor属性,指向Object构造函数,我们这儿就不可以经过constuctor来确定目的的档次了。

能够通过Person.prototype.constructor = Person复苏constructor的指针。

原型的动态性

原型在查找值的长河中是一次搜索,当我们想引用一个对象的某个属性时,所引述到的是原型链中包含该属性名的第一个对象所对应的属性值。换句话说,直接引用这么些特性的目标会首先被询问是否含有该属性名,假若含有,该属性值就是大家想得到的,查询截止,倘诺不分包,会随着查询该目的的原型是否含有该属性,依此类推。

我们可以随时动态地为原型添加属性和措施,而且,基于这种搜索过程,咱们对原型对象所做的另外修改都能及时从目的实例上寓目,即使该修改是在创设实例之后。但假如是用地方提到的语法重写整个原型对象就另当别论了。因为重写原型对象会切断现有原型对象与原来早就存在的其余对象实例之间的维系,它们包含的指针[[prototype]]如故指向原来的原型对象,我们可以看看上面的小例子。

01 function Person(){}
02 var person1 = new Person();
03 Person.prototype = {
04   name: "bella",
05   age: 21,
06   sayHello: function(){
07     alert(this.name);
08   }
09 }
10 person1.sayHello();  //error

地点的例证中,大家先创建了Person的一个实例对象person1,然后重写了Person的原型对象,之后再调用person1.sayHello()就会发出错误。因为person1中带有的指针[[prototype]]照例指向原来的原型对象,并不包含新的原型对象中定义的sayHello属性。

原型的问题

原型格局使得所有目标实例在默认情状下拿到一致的属性值,对于属性值为函数的图景,这正是大家愿意见到的,所有目的实例共享这一函数而不需要再一次定义,不过对于属性值为基本值的情事,大家日常希望不同的靶子实例拥有不同的基本值,但是,我们可以经过在目的实例上添加同名属性来掩藏原型对象中的属性。不过,倘若带有引用类型值的习性,问题就显现出来了。

01 function Person(){}
02 Person.prototype = {
03   name: "bella",
04   age: 21,
05   classmates: ["Lucy""Lily"],
06   sayHello: function(){
07     alert(this.name);
08   }
09 }
10 var person1 = new Person();
11 var person2 = new Person();
12 person1.classmates.push("Mark");
13 alert(person1.classmates === person2.classmates);  //true

此间,大家为Person.prototype对象添加了classmates属性,值为一个字符串数组,然后创设了四个目的实例person1,
person2。由于person1,
person2所具有的classmates属性其实是共享原型对象Person.prototype的classmates属性得到的,也就是数组只设有于Person.prototype对象中,person1和person2引用的是同一个数组,对person1中classmates的改动也会从person2.classmates中反映出去,那样会导致所有目标实例共享一个数组,这往往不是我们想要的。

以上,我只是简单地剖析了原型链的定义和原型对象的主干特色,希望能对大家有很小协助,想要更深厚地认识它,当然仍然得靠大家在实际上项目中去上学

原稿地址:

http://cube.qq.com/?p=163