我們建立的每一個函數都有一個prototype(原型)屬性,該屬性是一個對象,包含可以有特定類型的所有執行個體共用的屬性和方法。使用它的好處就在於可以讓所有對象執行個體共用它所包含的屬性和方法,也就是說,不必在建構函式中定義對象的資訊,而是可以將這些資訊,直接添加在原型對象中,如下所示,還是接著改寫前兩篇日誌中的例子:
複製代碼 代碼如下:function Employee() {
};
Employee.prototype.Name = "Jim";
Employee.prototype.Age = 28;
Employee.prototype.Job = "SoftWare Engineer";
Employee.prototype.SayName = function () {
alert(this.Name);
};
var employee1 = new Employee();
employee1.SayName();//Jim
var emplayee2 = new Employee();
emplayee2.SayName(); //Jim
alert(employee1.SayName = emplayee2.SayName);//true
與建構函式模式不同的是,新對象的這些屬性和方法是由所有執行個體共用的。
以上即是原型模式的一個引子,要理解原型模式的工作原理,就需要瞭解ECMASCRIPT中原型的性質。
理解原型
在Javascript中,只要建立了一個新函數,就會根據一組特定的規則為該函數建立一個prototype屬性。在預設的情況下,雖有prototype屬性都會自動獲得一個constructor屬性,這個屬性包含一個指向prototype屬性所在函數的指標,而通過這個建構函式,我們還可以繼續為原型添加其他屬性和方法。
建立了自訂的建構函式之後,其原型屬性預設只會取得constructor屬性,而至於其他的方法,則都是從Object繼承來的。當調用建構函式建立一個新執行個體後,該執行個體的內部將包含一個指標(內部屬性),指向建構函式的原型屬性。要注意的是這個串連存在於執行個體和建構函式原型屬性之間,而不是存在於執行個體與建構函式之間。
在某些實現中,無法訪問到內部屬性(_proto_屬性),但是在所有實現中都可以通過isPrototypeOf方法來確定對象之間是否存在這種原型關係。從本質上來看,如果對象的_proto_屬性指向isPrototypeOf,這個方法就返回true。如下所示: 複製代碼 代碼如下:alert(Employee.prototype.isPrototypeOf(employee1)); //true
alert(Employee.prototype.isPrototypeOf(employee2));//true
每當代碼讀取某個對象的某個屬性時,都會執行搜尋,目標是具有給定名字的屬性。搜尋最先從對象執行個體本身開始。如果在執行個體中找到具有給定名字的屬性,則然後該屬性的值,如果沒有找到,則繼續搜尋指標指向的原型對象,在原型對象中尋找具有給定名字的屬性。如果在原型對象中找到這個屬性,則返回該屬性的值。這也正是對個對象執行個體共用原型所儲存的屬性和方法的基本原理。
前面說過,原型最初只包含constructor屬性,而該屬性也是共用的,因此可以通過對象執行個體訪問
雖然可以通過對象執行個體訪問儲存在原型中的值,但是不能通過對象執行個體重寫原型中的值,如果我們在執行個體中添加一個屬性,而該屬性與執行個體原型中的一個屬性名稱相同,name在執行個體中建立的屬性會屏蔽(.net成為隱藏)原型中的那個屬性,如下所示: 複製代碼 代碼如下:function Employee() {
};
Employee.prototype.Name = "Jim";
Employee.prototype.Age = 28;
Employee.prototype.Job = "SoftWare Engineer";
Employee.prototype.SayName = function () {
alert(this.Name);
};
emplayee2.Name = "Sun";
alert(employee1.Name); //Jim
alert(employee2.Name);//Sun
其中employee1.Name的Jim來自原型,二employee2.Name的sun來自執行個體。