我们知道,js中每创建的一个函数都会有自己的prototype(原型)属性,这个属性本质上是一个指针,指向原型对象。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法,最简单的说,如果我们需要两个对象,这两个对象有5个属性是共有的,如果分开写每个对象代码会写上十行,如果使用原型,只需要在原型中写好这5行共有的,实例化对象的时候这两个对象都会包含需要的属性的。如果有很多对象有相同属性的时候,原型的方法会帮我们简化工作。
看下面的这个例子:
function Person(){
Person.prototype.name = "Joy";
Person.prototype.age = 29;
Person.prototype.job = "Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
person1.sayName(); // Joy
var person2 = new Person();
alert(person1.sayName = person2.sayName); //true
上面的例子很简单,我们从内部来理解构造函数,原型对象和这两个实例化的对象之间的关系。
function Person():
我们知道Person是一个函数,由于我们之前说过只要是一个函数就会有prototype属性,并且这个属性的值指向原型对象Person Prototypre,这就是Person函数中唯一有的东西。
Person Prototype原型对象:
在默认情况下,原型对象中会自动获得一个constructor属性,这个属性的值是一个指针,指向prototype所在函数,具体到这个例子来说就是Person函数,到此为止,已经有两条指针,一条是Person函数中prototype属性指向原型对象,一条是原型对象中的constructor,指回原来的拥有prototype属性的函数Person。注意是指向这个整个函数,另外原型对象中的其他的值还有name,age,job和一个方法sayName,里面的值分别为代码中相应的赋值。
person1和person2对象:
由于person1和person2 是用new操作符通过构造函数Person函数创建出来的,所以prototype属性自然也会有指向原型对象的指针,至此构造函数自己,和创建出来的这两个实例化的对象共有三条指针指向原型对象Person Prototype。也正是有指针指向原型对象,所以才可以对实例化出来的对象访问原型中有的属性和方法,而且两个实例化的对象访问出来的结果是一样的。这一点已经在上面的代码中验证了。
ECMASCript 5 中添加了一个新方法,叫Object.getPrototypeOf() , 用来返回prototype的值,例如我们想知道person1中prototype的值到底是不是Person prototype:
alert( Object.getPrototypeOf(person1) == Person.prototype ); //true
alert( Object.getPrototypeOf(person1.name); // Joy 这里直接用这个方法取得了原型的值了。
由于现在原型中有属性和方法,实例中也可以访问原型中的属性和方法,就会出现一下问题:
1. 可以通过对象访问原型中的值,那么可以通过对象实例改写原型中的值吗? 答案:否。
2. 访问具体对象的属性的搜索过程是怎么样的? 答案:先搜索实例化对象中是否有这个属性,再搜索原型中是否有这个属性。
3. 原型中的属性,实例化的时候再写,会覆盖掉吗? 答案:会,因为搜索过程在上面解释了,不过可以用delete操作符删除实例化中的属性,然后就可以重新访问原型中的属性了。
4. 有没有区分某个属性是实例化对象中的还是原型中的? 答案: 有,hasOwnPrototype()方法,参数是方法名,如果是实例中的返回true,如果是原型中的返回fasle。
最后补充一下原型的简化语法,上面用的写法有点麻烦了:
function Person(){
}
Person.prototype = {
name: "Joy";
age : 29;
job: "Engineer";
sayName : function(){
alert(this.name);
}
};
注: 例子来源于红宝书,也算是我对红宝书中对原型的介绍做一个梗概和整理了吧。