以下内容来自《JavaScript高级程序设计》第三版
我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。
function Person(){ } Person.prototype.name=‘lisi‘; Person.prototype.age=23; Person.prototype.job=‘Software engineer‘; Person.prototype.sayName=function(){ alert(this.name); } var person1 = new Person(); alert(person1.name);//lisi var person2 = new Person(); alert(person2.name);//lisi alert(person1.sayName==person2.sayName);//true
1、理解原型对象
只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包括一个指向prototype属性所在函数的指针。就像前面例子,Person.prototype.constructor指向Person。通过这个构造函数,我们还可以继续为原型对象添加其他属性和方法。
创建了自定义的构造函数之后,其原型对象默认只会取得constructor属性;至于其他方法,则都是从Object继承而来的。当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。
虽然在所有访问中都无法访问[[Prototype]],但可以通过isPrototypeOf()方法来确定对象之间是否存在这种关系。从本质上讲,只要[[Prototype]]指向调用isPrototypeOf()方法的对象(Person.prototype),那么这个方法就返回true。
alert(Person.prototype.isPrototypeOf(person1));//true alert(Person.prototype.isPrototypeOf(person2));//true
ECMAScript5增加了一个新方法,叫Object.getPrototypeOf(),在所有支持的实现中,这个方法返回[[Prototype]]的值。
alert(Object.getPrototypeOf(person1)==Person.prototype);//true
每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先从对象实例本身开始。如果在实例中找到具有给定名字的属性,则返回该属性的值;如果没找到,则继续搜索指针执行的原型对象,在原型对象中查找具有指定名字的属性。如果在原型对象中找到了该属性,则返回该属性的值。也就是说,当我们调用person1.sayName()方法的时候,会先后执行两次搜索。首先,会搜索实例person1的sayNane方法,没有继续搜索person1原型的sayName方法。
虽然可以通过实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。如果我们在实例中添加了一个属性,该属性与实例原型中的一个属性同名,那我们就在实例中创建该属性,该属性会屏蔽原型中的属性。
function Person(){ } Person.prototype.name=‘lisi‘; Person.prototype.age=23; Person.prototype.job=‘Software engineer‘; Person.prototype.sayName=function(){ alert(this.name); } var person1 = new Person(); alert(person1.name);//lisi var person2 = new Person(); person2.name=‘zhangsan‘; alert(person2.name);//zhangsan
即使将实例属性设置为null,也只会在实例中设置这个属性,而不会恢复其指向的原型的链接。不过,使用delete操作符可以完全删除实例属性,从而让我们能够重新访问原型中的属性。
var person1 = new Person(); alert(person1.name);//lisi var person2 = new Person(); person2.name=null; alert(person2.name);//null delete person2.name; alert(person2.name);//lisi
使用hasOwnProperty()方法可以检测一个属性是存在实例中还是原型中。这个方法(不要忘了是从Object继承来的)只在给定属性存在与对象实例中时返回true。
function Person(){ } Person.prototype.name=‘lisi‘; Person.prototype.age=23; Person.prototype.job=‘Software engineer‘; Person.prototype.sayName=function(){ alert(this.name); } var person1 = new Person(); alert(person1.hasOwnProperty(‘name‘));//false person1.name = ‘wangwu‘; alert(person1.name);//wangwu alert(person1.hasOwnProperty(‘name‘));//true var person2 = new Person(); alert(person2.name);//lisi alert(person2.hasOwnProperty(‘name‘));//false delete person1.name; alert(person1.name);//lisi alert(person1.hasOwnProperty(‘name‘));//false
2、原型与in操作符
有两种方式使用in操作符:单独使用和在for-in中使用。在单独使用时,in操作符会在通过对象能够访问给定属性时返回true,无论该属性是存在实例中还是原型中。
function Person(){ } Person.prototype.name=‘lisi‘; Person.prototype.age=23; Person.prototype.job=‘Software engineer‘; Person.prototype.sayName=function(){ alert(this.name); } var person1 = new Person(); alert(person1.hasOwnProperty(‘name‘));//false alert(‘name‘ in person1);//true person1.name = ‘wangwu‘; alert(person1.name);//wangwu alert(person1.hasOwnProperty(‘name‘));//true alert(‘name‘ in person1);//true
同时使用hasOwnProperty()和in操作符,就可以确定该属性到底存在与实例中还是原型中。
function hasPrototypeProperty(object,name){ return !object.hasOwnProperty(name) && (name in object); }
只要in操作符返回true而hasOwnProperty()返回false,就可以确定属性是原型中的属性。
在使用for-in循环时,返回的是所有能过通过对象访问的、可枚举的属性,其中既包括存在实例中的属性,也包括存在于原型中的属性。
要取得对象上所有的可枚举的实例属性,可以使用ECMAScript5的Object.keys()方法;
function Person(){ } Person.prototype.name=‘lisi‘; Person.prototype.age=23; Person.prototype.job=‘Software engineer‘; Person.prototype.sayName=function(){ alert(this.name); } var keys = Object.keys(Person.prototype); alert(keys);//name,age,job,sayName var person1 = new Person(); person1.name=‘zhangsan‘; person1.age=21; var pkeys = Object.keys(person1); alert(pkeys);//name,age
如果你想要得到所有的实例属性,无论它是否可枚举,都可以使用Object.getOwnPropertyNames()方法。
var keys = Object.getOwnPropertyNames(Person.prototype); alert(keys);//constructor,name,age,job,sayName