本文详细解析JavaScript中创建对象的七种核心方式,涵盖内置对象、工厂模式、构造函数、原型模式及其优缺点、混合模式、字面量、拷贝模式及第三方框架应用。通过对比各模式的特性与适用场景,帮助开发者根据实际需求选择最优方案,避免原型共享导致的引用修改问题,提升代码质量。
JavaScript对象创建基础与内置方式
JavaScript提供了内置对象创建的基础方式,通过new Object()实例化并直接赋值属性。例如,创建一个girl对象并赋予name属性为'hxl',随后通过typeof检查其类型。这种方式简单直接,适用于快速定义单一对象实例,但缺乏复用性,不适合批量创建结构相似的对象。
var girl=new Object();
girl.name='hxl';
console.log(typeof girl);
工厂模式与构造函数模式解析
工厂模式通过函数封装对象创建逻辑,内部使用new Object()实例化,设置属性并定义方法,最后返回该实例。以Person函数为例,接收name参数,内部创建p对象,赋予name属性及say方法,返回p。调用时通过Person('haoxioli')获取实例并执行say方法。此模式解决了多次创建相似对象的问题,但无法识别对象的具体类型,所有实例均为Object类型。
function Person(name){
var p=new Object();//内部进行实例化
p.name=name;
p.say=function(){
console.log('my name is '+ p.name);
}
return p;//注:一定要返回
}
var girl=Person('haoxioli');
girl.say();
构造函数模式利用new关键字调用函数,自动创建this对象并返回。定义Product函数时,通过this.name和this.buy方法直接绑定到实例。实例化时如var pro=new Product('真维斯'),pro直接拥有name和buy属性。这种方式允许识别对象类型(Product),且每个实例拥有独立的方法引用,但方法重复创建导致内存浪费。
var Product=function(name){
this.name=name;
this.buy=function(){
console.log('我衣服的牌子是'+this.name);
}
}
var pro=new Product('真维斯');
pro.buy();
原型模式及其引用共享问题
原型模式将属性和方法定义在构造函数的prototype对象上,所有实例共享原型中的属性。例如Role构造函数为空,其prototype.name包含nickName属性。实例boy和girl共享该原型,修改boy.name.nickName会直接影响girl.name.nickName,因为它们是引用类型值的共享。这揭示了原型模式的缺点:当属性为引用类型时,实例间的修改会相互影响,导致数据不一致。
var Role=function(){}
Role.prototype.name={nickName:'昵称'};
var boy=new Role();
boy.name.nickName='刘晓兵';
console.log(boy.name.nickName);//刘晓兵
var girl=new Role();
girl.name.nickName='郝晓利';
console.log(boy.name.nickName);//郝晓利,因为实例对象可以修改原型中的引用对象的值
console.log(girl.name.nickName);//郝晓利
混合模式:构造函数与原型的结合
混合模式结合构造函数与原型,旨在解决原型模式引用共享问题及构造函数模式方法重复问题。在构造函数中定义引用类型属性(如name对象),确保每个实例拥有独立的属性副本;在原型中定义非引用类型属性(如age)或方法。例如,Role构造函数中this.name为独立对象,原型中age为30。实例boy和girl修改各自的name.nickName互不影响,体现了混合模式在数据隔离与资源共享间的平衡。
var Role=function(){
this.name={nickName:'aaa'};
}
Role.prototype.age=30;
var boy=new Role();
boy.name.nickName='boy';
console.log(boy.name.nickName);//boy
var girl=new Role();
girl.name.nickName='girl';
console.log(boy.name.nickName);//boy,实例不会修改构造函数中的值
console.log(girl.name.nickName);//girl
字面量与JSON序列化应用
字面量形式是创建对象最简洁的方式,直接通过大括号{}定义属性与方法。如cat对象包含name、age、sex属性。此外,字面量常与JSON序列化结合使用,通过JSON.stringify将对象转为字符串,或JSON.parse将字符串转为对象。这种方式适用于配置项、数据传输及简单对象定义,无需new关键字,语法直观。
var cat={
name:'lucy',
age:3,
sex:'母'
};//将对象转换成字符串
console.log(JSON.stringify(cat));//{"name":"lucy","age":3,"sex":"母"}
var dog='{"name":"john","sex":"公"}';
console.log(JSON.parse(dog).name);//将字符串转为对象
对象拷贝模式实现
拷贝模式通过遍历源对象属性并赋值给目标对象,实现对象属性的浅拷贝。extend函数接收tar和source,遍历source的每个属性i,将其值赋给tar[i],最后返回tar。例如,将boy对象{name:'醉侠客',age:20}拷贝至新对象person。此方法适用于需要创建对象副本且属性为基本类型或无需深层共享的场景,但需注意引用类型属性的共享问题,深层拷贝需额外处理。
function extend(tar,source){
for(var i in source){
tar[i]=source[i];
}
return tar;
}
var boy={name:'醉侠客',age:20};
var person=extend({},boy);
console.log(person.name);
第三方框架简化对象创建
第三方框架base2通过Base.extend简化对象创建,固定使用constructor定义初始化逻辑,其他方法直接定义。引入base2.js后,定义Animal类,constructor接收name,say方法输出name与消息。实例化new Animal('lily').say('fish')执行。框架封装了继承与原型操作,提升开发效率,适用于大型项目或需要复杂继承结构的场景,但依赖外部库增加体积。
//先引入包
<script src='js/base2.js'></script>
//base2框架,Base.extend及constructor都是固定用法
var Animal=Base.extend({
constructor:function(name){
this.name=name;
},
say:function(meg){
console.log(this.name+":"+meg);
}
});
new Animal('lily').say('fish');
另一第三方框架simplejavascriptinheritance采用Class.extend与init固定用法。引入simplejavascriptinheritance.js后,定义Person类,init方法处理初始化。实例化Person后,可直接修改name属性。此类框架提供简洁的API,隐藏底层原型链细节,适合快速原型开发。然而,框架特定语法可能增加学习成本,且需权衡库大小与功能需求。
<script src='js/simplejavascriptinheritance.js'></script>
//simplejavascriptinheritance框架,Class.extend及init都是固定用法
var Person=Class.extend({
init:function(name){
this.name=name;
}
});
var p=new Person();
p.name='over';
console.log(p.name);
综上所述,JavaScript提供了从基础内置对象到复杂框架的多种对象创建方式。开发者应根据项目需求、性能考量及维护成本,选择合适的模式。内置与字面量适合简单场景,工厂与构造函数适用于基础复用,原型与混合模式平衡共享与隔离,拷贝模式处理副本,框架简化复杂继承。理解各模式特性,有助于编写更健壮、高效的JavaScript代码。