每個函數都有一個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引用的仍然是最初的原型。