JavaScript 批量创建对象的五种核心模式详解
本文深入解析JavaScript中批量创建对象的五种主要方式。从基础的工厂模式到构造函数模式,再到解决内存共享问题的原型模式,最后介绍结合两者优势的组合模式以及动态原型、寄生和稳妥构造函数模式。通过代码示例对比各模式的优缺点,帮助开发者根据实际场景选择最优方案,避免资源浪费与数据共享陷阱。
前言
无论使用何种编程语言,其核心原理万变不离其宗,只有深入理解本质,才能实现灵活应用。在JavaScript中,闭包、原型链、函数以及对象等概念需要投入大量精力去思考和掌握。本文将结合JavaScript的原型机制与函数特性,探讨批量创建对象的多种实现方式,并分析各自的优劣。
正文
工厂模式:直观但无法标识类型
提到创建对象,最直观的方法莫过于使用对象字面量直接定义。然而,这种方式仅适用于创建少量、独立且彼此无关的对象。如果需要批量生成具有相似结构的对象,应当采用何种策略?
工厂模式
工厂模式的实现非常直观,它将对象创建的逻辑封装在一个函数中,通过特定的接口隐藏创建细节。具体实现如下:
function createStudent(name,sex,grade){
var o = new Object();
o.name = name;
o.sex = sex;
o.grade = grade;
o.sayName = function(){
console.log(this.name);
}
return o;
}
var s1 = createStudent('Claiyre','famale',1);
通俗来说,工厂模式是将创建对象的代码放入一个函数内部,通过接收参数来构建特定对象,最终返回该对象实例。
尽管工厂模式能够创建多个相似对象,但它无法解决对象类型标识的问题,即无法准确判断一个对象的具体类型。为此,构造函数模式应运而生。
构造函数模式:解决类型标识与内存浪费
构造函数模式
构造函数模式是Java语言中创建对象的标准方式。JavaScript与Java在使用构造函数创建对象时存在细微差异,需注意区分。
JavaScript中没有类的概念,函数作为一等公民,无需显式声明类,直接定义构造函数即可。类的属性和方法通常在构造函数内部或原型对象上定义。构造函数模式的示例代码如下:
function Student(name,sex,grade){
this.name = name;
this.sex = sex;
this.grade = grade;
this.sayName = function(){
console.log(this.name);
}
}
var s2 = new Student('孙悟空','male',2);
细心的读者可能注意到,构造函数的函数名首字母通常大写,而普通函数首字母小写。这是众多面向对象语言的约定俗成。虽然不遵循此规范通常不会报错,但为了代码的规范性与可读性,建议将构造函数首字母大写,以与普通函数区分。
与工厂模式相比,构造函数模式在创建对象时有以下显著区别:
- 没有显式地创建对象实例
- 直接将属性和方法赋值给this对象
- 没有return语句
此外,创建Student实例时必须使用new操作符。创建的实例对象包含一个constructor属性,指向Person构造函数。调用构造函数创建对象的过程如下:
- 创建一个新对象
- 将构造函数的作用域赋给新对象(此时this指向该新对象)
- 执行构造函数中的代码
- 返回新对象(无需显式返回)
构造函数虽然实用,但也存在缺点。主要问题在于每个方法都要在每个实例上重新创建一遍。在ECMAScript中,函数即对象,因此每定义一个函数,实际上就是实例化了一个对象。以下示例证明了这一缺点。
var s3 = new Student('唐僧','male',3);
var s4 = new Student('白骨精','female',4);
s3.sayName();
s4.sayName();
console.log(s3.sayName == s4.sayName);
运行结果:

这意味着,通过构造函数实例化的多个对象,其方法虽然是不同的函数实例,但内部代码和功能完全相同,这导致了不必要的资源浪费。
幸运的是,这一问题可以通过原型模式来解决。
原型模式:共享属性与引用类型陷阱
原型模式
在JavaScript中,每个函数都有一个prototype属性,它是一个指针,指向一个对象,称为原型对象。原型对象包含了可以由特定类型的所有实例共享的属性和方法。此外,该对象天生具有一个constructor属性,指向创建对象的构造方法。
使用原型模式可以让所有实例共享原型对象中的属性和方法,这意味着无需在构造函数中定义实例信息。代码表示如下:
function Student_1(){
}
Student_1.prototype.name = 'Claiyre';
Student_1.prototype.sex = 'female';
Student_1.prototype.class = 5;
Student_1.prototype.sayName = function (){
console.log(this.name);
}
var s5 = new Student_1(); s5.sayName(); //Claiyre
var s6 = new Student_1();
s6.sayName(); //Claiyre
一张图胜过千言万语,下图清晰地阐释了各个对象和原型对象间的关系:

了解原型机制后,可以在实例对象上继续添加属性或方法:
s6.name = 'John';
s6.sayName(); //John
当读取某个对象的属性时,会执行一次搜索。搜索首先从对象实例本身开始,如果在实例中找到该属性,则搜索结束并返回实例属性的值;若实例中未找到,则继续向对象的原型对象延伸,搜索原型对象。若在原型对象上找到,则返回原型上相应属性的值;若仍未找到,则返回undefined。因此,实例对象属性会覆盖原型对象上的同名属性,上述第二行代码输出的是John。
- Object.getPrototypeOf(object)方法返回参数对象的原型对象。
- Object.keys(object)方法返回对象上可枚举的实例属性。
原型中的所有属性都被所有实例共享。这种共享机制对于函数来说非常合适,对于包含基本值的属性也勉强可行(因为实例属性会覆盖原型同名属性),但对于包含引用类型的属性,则可能引发严重问题。
Student_1.prototype.friends = ['aa','bb'];
console.log('s6的朋友' + s6.friends);
s5.friends.push('cc');
console.log('s5的朋友' + s5.friends);
console.log('s6的朋友' + s6.friends);
运行结果:

问题出现了:我们仅希望修改s5的朋友列表,但由于原型模式的共享本质,s6的朋友列表也随之改变。
因此,原型模式很少单独使用。
组合模式:构造函数与原型的优势结合
组合使用构造函数和原型模式
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享属性。这种混合模式不仅支持向构造函数传入参数,还最大限度地节约了内存,可谓集两种模式之长。示例代码如下:
function Student(name,sex,grade){
this.name = name;
this.sex = sex;
this.grade = grade;
}
Student.prototype.sayName = function(){
console.log(this.name);
}
Student.prototype.school = 'Joooh school';
其他模式
其他创建模式:动态原型、寄生与稳妥构造函数
除了上述常见模式外,批量创建对象的方式还包括:
- 动态原型模式:仅在第一次调用构造函数时,将方法赋给原型对象的相应属性,其他示例的处理方式同构造函数模式。
- 寄生构造函数模式:仅封装创建对象的代码,然后返回新创建的对象,仍使用new操作符调用。
- 稳妥构造函数模式:没有公共属性,只有私有变量和方法,以及一些get/set方法,用以处理私有变量。
结语
每种模式都有其优缺点,具体使用哪种方式,还需结合实际场景,深入理解并灵活运用。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持本站!
JavaScript闭包与类(Class)的区别及适用场景对比
2026-11-05 15:48:57
JavaScript入门要学ES6吗?let与箭头函数等新特性解析
2026-11-05 14:49:16
JavaScript 8种数据类型详解:基本类型与引用类型的区别
2026-11-05 14:25:15
怎么在JavaScript中使用async/await优化Ajax异步流程
2026-11-05 13:56:15
JavaScript replace回调函数实现动态正则替换逻辑
2026-11-05 13:52:13
