構造函數:函數中的一種,通過關鍵字new可以創建其實例。為了便於區分,通常首字母大寫;
原型對象:一種特殊的對象,構造函數創建時自動生成;與構造函數形成一一對應,如同人和影子般的關系;
實例:通過構造函數實例出來的對象;
在定義構造函數時,在其內部(“{“和”}”)進行定義屬性和方法。當我們通過關鍵字new,對構造函數進行實例化的時候。實例會對構造函數的這些屬性進行拷貝出一份副本,然后將其歸屬為當前實例。不同實例間的屬性和方法是完全獨立的,如下例子:
function Person(name,age){ this.name = name; this.age = age; this.sayName = function(){ alert(this.name); }; } var HL = new Person("hailing",18); var JJ = new Person("jiajia",20); HL.sayName(); // hailing JJ.sayName(); // jiajia HL.name = "HAILING"; HL.sayName(); // HAILING JJ.sayName(); // jiajia
我們通過創建了new對Person進行實例化兩個對象“HL”、“JJ”,當我們在修改“HL”的名字時,“JJ”的名字不會隨之而改變。
程序執行時,會自動生成一個原型對象(prototype)與之相關聯。同時給該構造函數自動添加一個屬性:prototype,該屬性為指針,指向原型對象。同時,也給該原型對象添加一個屬性:constructor,該屬性也為指針,指向與其對應的構造函數。此時,原型對象中只會包含些默認的方法和屬性。
實例化完成后,所有實例均會與原型對象形成多對一的隱性關聯關系。所有實例會共享原型對象的屬性和方法,當然也包括constructor。當原型對象被添加一個屬性或者方法后,均會被所有實例共享,即可以通過任意一個實例進行訪問。如果原型對象的屬性或方法與實例的屬性或方法名稱一致,則實例自身的屬性或方法優先級高於原型對象上的:
//添加公共的屬性和方法 Person.prototype.sex = "girl"; Person.prototype.saySex = function (){ alert(this.sex); }; alert(HL.saySex === JJ.saySex); // true alert(HL.saySex === Person.prototype.saySex); // true Person.prototype.stature = 165; alert(HL.stature); // 165 alert(JJ.stature); // 165 //為實例對象修改或者添加私有屬性。 HL.stature = 160; alert(HL.stature); // 160 alert(JJ.stature); // 165 //刪除實例對象的私有屬性。 delete HL.stature; Person.prototype.stature = 170; alert(HL.stature); // 170 alert(JJ.stature); // 170
例子中,給原型對象增加了sex屬性和saySex方法,此時通過實例對象“HL”和“JJ”訪問並且進行比較,同時也和原型對象自身的saySex進行比較,結果為ture;而后又給原型對象增加了stature屬性,此時“HL”和“JJ”中均無此屬性,故訪問了原型對象中的stature屬性。當我們單獨給“HL”增加了stature屬性后,“HL”不在訪問原型對象,而是訪問“HL”自身的該屬性。當我們通過關鍵字“delete”刪除“HL”自身的屬性后,再次訪問時,又回重新訪問原型對象上的屬性。
總結: 構造函數中的屬性和方法僅為聲明和定義,一旦實例化工作完成后。實例對象自身的屬性和方法與構造函數將不在存在關聯關系。原型對象與實例對象形成“備胎”關系,當通過對象訪問屬性或方法時,程序會優先搜索對象本身的屬性或方法,不存在才會訪問原型對象的方法或者屬性。
因此,當多個實例需要使用同一個屬性或方法時,我們應該將該方法放於原型對象上,從而避免相同屬性或方法多次獨立存在於多個對象導致內存浪費。