每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。使用原型对象的好处是让所有的对象实例共享它包含的属性和方法。
每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定的名字的属性,搜索首先是从对象实例本身开始。如果在实例中找到了具有给定的名字的属性时,则会返回该属性的值;如果在没有找到,则继续搜索指针指向原型对象,在原型对象中查找育有特定名字的属性。如果在原型对象中找到了这个属性,则返回该属性的值。
function Person(){
}
Person.prototype.name="Nacholas";
Person.prototype.age=29;
Person.prototype.job="Software Engineer";
Person.prototype.sayName=function(){
alert(this.name);
};
var person1=new Person();
var person2=new Person();
person1.name="Gred";
alert(person1.name);//"Gred"--来自实例
alert(person2.name);//"Nicholas"--来自原型
上面的例子每添加一个属性和方法就要敲一次Person.prototype.为减少不必要的输入,为了更好地封装原型的功能,更常见的做法是用一个包含所有属性和方法的对象字面量来重写整个原型对象。代码如下:
function Person(){
}
Person.prototype={
name:"Nacholas",
age:29,
job:"Software Engineer",
sayName:function(){
alert(this.name);
}
}
由于在原型中找值的过程是一次搜索,所以我们对原型对象所做的任何修改都能够立即在实例中反映出来——即使是先创建了实例后再修改原型也照样如此。
var friend=new Person();
Person.prototype.sayHi=function(){
alert("hi");
};
friend.sayHi(); // "Hi"
当我们在调用person.sayHi()时,首先是在实例中搜索名为“sayHi”的属性,在没有搜到的情况下,会继续搜索原型。因为实例与原型之间的连接只是一个指针,而不是一个副本,因此就可以在原型中找到新的sayHi()属性并返回保存在那里的函数。
但是如果是重写了整个原型对象的话情况就会不一样的了。调用构造函数会为实例添加一个指向最初原型的【prototype】指针,而把原型修改为另外一个对象就等于切断了构造函数与最初原型之间的联系。如:
function Person(){
}
var friend=new Person();
Person.prototype={
constructor:Person,
name:"Nacholas",
age:29,
job:"Software Engineer",
sayName:function(){
alert(this.name);
}
}friend.sayName(); //error
上面的例子,我们先是创建了一个person实例,然后又重写了其原型对象。然后再调用friend.sayName()方法时发生了错误。因为friend指向的原型中不包括以该名字命名的属性。friend引用的仍然是最初的原型。