建立物件指建立一個object並給這個物件新增屬性和方法,有以下幾個方式:
最基本的:
var Person={};
Person.name='tom';
Person.age='20';
Person.sayname=function(){
alert(this.name);
};
建立了一個Person物件,並新增了name,age屬性,還有一個sayname方法。
下面是用建構函式方式建立:
function Person(name,age){ this.name=name; this.age=age; this.sayname=function(){ alert(this.name); }; } var p1=new Person('yom',0); var p2=new Person('tom',99); alert(p1.name); //yom alert(p2.name); //tom alert(p1.constructor==p2.constructor);//true alert(p1 instanceof Person);//true alert(p2 instanceof Person);//true注意:此時例項化的兩個例項p1和p2都源自Person,這兩個例項都含有constructor屬性,該屬性指向其建構函式,alert(p1.constructor==p2.constructor);//true可證明,這種方法的不足之處在於兩個例項間相互獨立,倘如有大量例項,造成很大記憶體浪費。
下面用原型方式可以解決例項間的屬性不能共享的問題:
function Person(){} Person.prototype.name='tom'; Person.prototype.age=20; Person.prototype.sayname=function(){ alert(this.name); }; var p1=new Person(); alert(p1.name);//tom alert(p2.name);//tom alert(p1.name==p2.name);//true上述程式碼中p1和p2共享Person物件原型中的屬性,但,此時並不能通過例項來重寫原型中的屬性,因為一旦在例項中重新定義屬性後就會遮蔽原型中的屬性,
因為此時會優先使用例項中的屬性,如果例項中沒有該屬性,那就上找到原型,下面程式碼中p1使用的是例項中的name屬性,p2使用的是原型中的屬性:
function Person(){} Person.prototype.name='tom'; Person.prototype.age=20; Person.prototype.sayname=function(){ alert(this.name); }; var p1=new Person(); alert(p1.name);//tom alert(p2.name);//tom alert(p1.name==p2.name);//true p1.name='jim';//通過例項p1來重新定義name屬性,遮蔽了原型中的name(只遮蔽,不修改) alert(p1.name);//jim alert(p2.name);//依然是tom上面每新增一個屬性都要Person.prototype.XX=XX; 很麻煩,可以用字面量的方式一次性定義,但要注意一個問題,那會重寫預設的prorotype,使constructor不再指向Person
function Person(){} Person.prototype={ name:'tom', age:20, sayname:function(){ alert(this.name); } }; var p1=new Person(); var p2=new Person(); alert(p1.name);//tom alert(p2.name);//tom alert(p1.name==p2.name);//true p1.name='jim';//通過例項p1來重新定義name屬性,遮蔽了原型中的name(只遮蔽,不修改) alert(p1.name);//jim alert(p2.name);//依然是tom alert(p1.constructor==Person);//重寫了prototype,constructor不再指向Person,但可手動調整要想手動調整在prototype裡新增一句就好:
Person.prototype={ constructor:Person,//手動更改 name:'tom', age:20, sayname:function(){ alert(this.name); } };
手動更改好後:alert(p1.constructor==Person);為true
上面提到,通過p1來重新定義name屬性不會影響p2中的name,因為p1重新定義name屬性後該屬性屬於p1這個例項中的屬性,p2依然適用原型中的屬性,所以不受影響。這裡最核心的原因是因為name屬性的屬性值是js的基本資料型別(alert(typeof Person.name);為string),不是引用型別,倘若是引用型資料,那改動p1,p2就有影響了,這也是原型方式的不足之處:
function Person(){} Person.prototype={ constructor:Person, name:['tom','cat'],//name不再是基本資料型別,而是引用型Array age:20, sayname:function(){ alert(this.name); } }; var p1=new Person(); var p2=new Person(); alert(p1.name);//tom alert(p2.name);//tom p1.name.push('newname');//通過例項p1來修改引用型name屬性 alert(p1.name);//'tom','cat','newname' alert(p2.name);//p2也改變:'tom','cat','newname'造成p1,p2都會改變的原因是兩者都指向同一陣列。
下面就是相對來說最‘完美’,最常見的建立物件的方式:建構函式模式與原型模式並用。其優點是把共有的屬性和方法定義在原型中,把例項屬性定義在建構函式中,這樣,對於不同的例項來說,該共享的共享,該獨立的獨立。把引用型數值的屬性定義到建構函式中,也就解決了上述原型模式中的不足之處:
function Person(name,age){ this.name=name; this.age=age; this.money=[10,100,1000];//這是個引用型,要定義在建構函式中 } Person.prototype={ constructor:Person, sayname:function(){ alert(this.name); } }; var p1 = new Person('tom',20); var p2=new Person('cat',30); alert(p1.money);//10,100,1000 alert(p2.money);//10,100,1000 p1.money.push(10000); //p1存入10000元 alert(p1.money); //10,100,1000,10000 alert(p2.money); //p2的money依然不變,還是10,100,1000。此時不再受p1的影響
還有一種更加“智慧”的方式:動態原型模式
function Person(name,age){ this.name=name; this.age=age; this.money=[10,100,1000];//這是個引用型,要定義在建構函式中 if(typeof this.sayname!='function') { Person.prototype.sayname=function(){ //注意:此處不能用字面量的方式重寫prototype,否則切斷例項與新原型的關係 alert(this.name); }; } } var p1 = new Person('tom',20); var p2=new Person('cat',30); alert(p1.money);//10,100,1000 alert(p2.money);//10,100,1000 p1.money.push(10000); //p1存入10000元 alert(p1.money); //10,100,1000,10000 alert(p2.money); //p2的money依然不變,還是10,100,1000。此時不再受p1的影響 p1.sayname();//tom p2.sayname();//cat
除此之外還有寄生建構函式模式和工廠模式,兩者的區別是例項化例項的方式不同,其餘全部一樣:
所謂寄生就是在function裡建立物件,並給物件新增屬性後從function中返回該物件
function Person(name,age){ var o=new Object();//在函式裡建立物件 o.name=name; o.age=age; o.sayname=function(){ alert(this.name); }; return o; //新增完屬性後返回o } //寄生建構函式模式用new var p1 = new Person('tom',20);//寄生建構函式模式 //工廠模式直接呼叫Person函式 var p2=Person('cat',30); //工廠模式 alert(p1.age);//20 alert(p2.age);//30 p1.sayname();//tom p2.sayname();//cat
注意:如果用此文的第一種建構函式模式建立物件,用new和直接呼叫這兩種方式的區別在於this上,另寫一篇文章介紹。此文的敘述方式可能存在很多不足或錯誤,本人還是菜鳥階段,歡迎批評指正。