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