JavaScript 批量创建对象的五种核心模式详解

作者:半夏

时间:2026-10-21

浏览:641

本文深入解析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方法,用以处理私有变量。

结语

每种模式都有其优缺点,具体使用哪种方式,还需结合实际场景,深入理解并灵活运用。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持本站!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章