本文系统梳理 JavaScript 中创建对象的几种核心模式。从基础的直接赋值到工厂模式,再到构造函数法,深入分析了各自在内存管理与代码复用上的优缺点。重点讲解了原型模式的局限性及其引发的引用类型共享问题,最终引出结合构造函数与原型的组合模式,这是目前定义复杂对象最常用且高效的方法。
工厂模式与构造函数法
工厂模式
初级开发者在定义对象时,往往采用直接实例化的方式。
var obj = new Object();
obj.name = "hero";
obj.showName=function (){alert(this.name);}
这种写法存在明显的缺陷:当需要在程序多处复用该对象时,必须重复编写大量相似代码。为了解决代码冗余问题,工厂模式应运而生。
function createObj()
{
var obj = new Object();
obj.name="hero";
obj.showName=function (){alert(this.name);}
return obj;
}
var obj1 = createObj();
var obj2 = createObj();
除了工厂模式,另一种常见做法是使用构造函数法。
function obj (name)
{
this.name=name;
this.showName= function (){alert(this.name);}
}
上述方法将生成对象的逻辑封装起来,避免了重复使用 new 关键字。为了进一步提升灵活性,可以对 createObj 函数进行改进,使其接收参数而非使用固定的默认值。
function createObj(name)
{
var obj = new Object();
obj.name=name;
obj.showName=function (){alert(this.name);}
return obj;
}
var obj1 = createObj("hero");
var obj2=createObj("dby");
然而,这种方法存在一个性能隐患:每次调用 createObj 函数时,都会重新创建一个新的 showName 函数实例。这意味着每个对象都拥有自己独立的 showName 版本,造成了不必要的内存浪费。
function showName()
{
alert(this.name)
}
function createObj(name)
{
var obj = new Object();
obj.name=name;
obj.showName=showName;
return obj;
}
通过将函数定义在外部并赋值给对象属性,成功解决了重复创建函数的问题。
原型模式的局限性
原型方式
这种方法主要利用对象的 prototype 属性来实现。
function obj()
{}
obj.prototype.name="hero";
obj.prototype.showName=function()
{
alert(this.name);
}
虽然原型方式看起来比工厂模式更完美,但它存在一个关键缺陷:没有构造函数,属性是通过 prototype 指定的。在实际应用中,所有实例的属性值完全相同,这往往不符合需求。更严重的安全隐患在于引用类型的共享问题。
obj.prototype.nameArray = new Array("hero","dby");
然后
obj1 = new obj();
obj2 = new obj();
obj1.nameArray.push("lxw");
如果在 obj2 中查看 nameArray,会发现其中也包含了 "lxw"。这是因为 obj1 和 obj2 的 nameArray 指向的是内存中的同一个引用数组,修改其中一个会影响另一个。
因此,单纯的原型方式并非理想解决方案。
组合构造函数模式
需要改进
最佳实践是结合构造函数与原型:在构造函数中定义属性,在原型中定义方法。
例如
fuction obj(name)
{
this.name = name
this.nameArray = new Array("hero","dby");
}
obj.prototype.showName = function(){alert(this.name)}
在这种组合模式下,所有非函数属性都在构造函数内部创建,而函数属性则通过 prototype 方式创建。这样,obj1 修改 nameArray 的值不会影响 obj2 的 nameArray,同时所有对象共享同一个 showName 函数,既保证了数据隔离又避免了内存浪费。
这种模式基本达到了完美平衡,其他修饰性技巧可根据具体需求自行探索。
单例模式变体与完整实例
这里补充一个单例模式的变体示例:
function obj (name)
{
this.name = name;
this.nameArray=new Array("hero","dby");
if(typeof obj._initialized=='undefined')
{
obj.prototype.showName=function(){alert(this.name);}
obj._initialized="true";
}
}
这并非严格的单例模式,而是一种优化手段:在构造对象时,先判断某个属性是否已定义。若未定义,则使用原型方法定义对象函数;若已定义,则不再重复定义。该 prototype 方法仅被创建和赋值一次。
经过上述优化,方案已相当完善。
以上为个人理解,旨在提供有益参考。如有不完善之处,欢迎联系指正。
以下是一个完整的实例代码:
function Rectangle(name,color,width,heigth){
this.name=name;
this.color=color;
this.width=width;
this.heigth=heigth;
}
Rectangle.prototype.area=function(){
return this.width*this.heigth
}
Rectangle.prototype.show=function(){
document.write(this.name+" "+this.color+" "+this.width+" "+this.heigth+"<br/>");
document.write(this.area());
}
var obj1= new Rectangle("Rectangle","red",15,20);
obj1.show();}