js中創建對象的幾種方式

創建對象指創建一個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上,另寫一篇文章介紹。此文的敘述方式可能存在很多不足或錯誤,本人還是菜鳥階段,歡迎批評指正。









發佈了47 篇原創文章 · 獲贊 4 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章