在 JavaScript 中,对象是核心数据类型之一。由于其动态性和灵活性,创建对象的方式多种多样。每种方式都有其适用场景和局限性。
本文将系统梳理 6 种对象创建方式,深入剖析其原理、优缺点,并提供代码示例。
一、字面量创建(Object Literal)
✅ 基础用法
js
const person = {
name: 'Alice',
age: 25,
greet() {
console.log(`Hello, I'm ${this.name}`);
}
};
✅ 优点
- 语法简洁,可读性强;
- 适合创建单个对象。
❌ 缺点
- 创建大量相似对象时,代码重复严重;
- 无法复用。
js
// 重复代码!
const person1 = { name: 'Alice', age: 25 };
const person2 = { name: 'Bob', age: 30 };
const person3 = { name: 'Charlie', age: 35 };
✅ 适用场景:配置对象、单例、临时数据结构。
二、工厂模式(Factory Pattern)
✅ 核心思想:用函数封装创建细节
js
function createPerson(name, age) {
return {
name,
age,
greet() {
console.log(`Hello, I'm ${this.name}`);
}
};
}
const p1 = createPerson('Alice', 25);
const p2 = createPerson('Bob', 30);
✅ 优点
- 解决了代码重复问题;
- 可通过参数初始化对象。
❌ 缺点
-
无法识别对象类型 :
jsconsole.log(p1 instanceof createPerson); // ❌ false console.log(typeof p1); // "object" --- 无法区分具体类型
-
所有方法都在每个实例中重新创建,浪费内存。
✅ 适用场景:需要创建多种类型对象的工厂函数。
三、构造函数模式(Constructor Pattern)
✅ 核心:使用 new
关键字
js
function Person(name, age) {
this.name = name;
this.age = age;
this.greet = function() {
console.log(`Hello, I'm ${this.name}`);
};
}
const p1 = new Person('Alice', 25);
const p2 = new Person('Bob', 30);
✅ new
的执行过程
- 创建一个新对象;
- 将构造函数的
prototype
赋值给新对象的[[Prototype]]
; - 将
this
指向新对象; - 执行构造函数体;
- 如果函数没有返回对象,则返回新对象。
✅ 优点
-
可通过
instanceof
识别类型:jsconsole.log(p1 instanceof Person); // ✅ true
-
支持参数初始化。
❌ 缺点
-
方法在每个实例中重复创建 ,浪费内存:
jsconsole.log(p1.greet === p2.greet); // ❌ false
✅ 适用场景:需要类型识别的简单对象创建。
四、原型模式(Prototype Pattern)
✅ 核心:共享 prototype
上的属性和方法
js
function Person() {}
Person.prototype.name = 'Unknown';
Person.prototype.age = 0;
Person.prototype.greet = function() {
console.log(`Hello, I'm ${this.name}`);
};
const p1 = new Person();
p1.name = 'Alice';
p1.age = 25;
const p2 = new Person();
p2.name = 'Bob';
p2.age = 30;
✅ 优点
-
方法只在原型上创建一次,内存高效 :
jsconsole.log(p1.greet === p2.greet); // ✅ true
❌ 缺点
-
无法通过构造函数传参初始化;
-
引用类型属性被所有实例共享 :
jsPerson.prototype.hobbies = ['reading']; p1.hobbies.push('coding'); console.log(p2.hobbies); // ['reading', 'coding'] --- 被污染!
✅ 适用场景:方法共享,但不适合存储实例数据。
五、组合模式(构造函数 + 原型)------ 最佳实践
✅ 核心:属性在构造函数中定义,方法在原型上定义
js
function Person(name, age) {
this.name = name;
this.age = age;
this.hobbies = []; // 每个实例独有
}
Person.prototype.greet = function() {
console.log(`Hello, I'm ${this.name}`);
};
const p1 = new Person('Alice', 25);
const p2 = new Person('Bob', 30);
p1.hobbies.push('reading');
p2.hobbies.push('coding');
console.log(p1.hobbies); // ['reading']
console.log(p2.hobbies); // ['coding'] --- 互不影响
✅ 优点
- ✅ 属性私有,避免共享问题;
- ✅ 方法共享,节省内存;
- ✅ 支持类型识别;
- ✅ 支持参数初始化。
❌ 缺点
- 代码分散在构造函数和原型中,封装性差。
✅ 适用场景 :ES6 之前的标准对象创建方式。
六、动态原型模式(Dynamic Prototype Pattern)
✅ 核心:在构造函数内部初始化原型,仅一次
js
function Person(name, age) {
this.name = name;
this.age = age;
this.hobbies = [];
// 仅在第一次调用时初始化原型
if (typeof this.greet !== 'function') {
Person.prototype.greet = function() {
console.log(`Hello, I'm ${this.name}`);
};
}
}
const p1 = new Person('Alice', 25);
const p2 = new Person('Bob', 30);
✅ 优点
- 封装了组合模式的逻辑;
- 方法只创建一次;
- 支持类型识别。
❌ 缺点
- 破坏了构造函数和原型的"分离"原则;
- 代码可读性略差。
✅ 适用场景:希望完全封装对象创建逻辑。
七、寄生构造函数模式(Parasitic Constructor Pattern)
✅ 核心:扩展已有类型,不修改原构造函数
js
function SpecialArray() {
const arr = new Array();
// 扩展功能
arr.first = function() {
return this[0];
};
arr.last = function() {
return this[this.length - 1];
};
// 参数处理
for (let i = 0; i < arguments.length; i++) {
arr.push(arguments[i]);
}
return arr;
}
const special = new SpecialArray(1, 2, 3);
console.log(special.first()); // 1
console.log(special.last()); // 3
✅ 优点
- 可以基于原生类型(如 Array、Date)进行扩展;
- 不污染原构造函数。
❌ 缺点
-
无法通过
instanceof
识别类型:jsconsole.log(special instanceof SpecialArray); // ❌ false
-
本质上还是工厂模式。
✅ 适用场景:需要扩展原生对象但不修改其原型。
八、ES6 Class(现代方式)
js
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
this.hobbies = [];
}
greet() {
console.log(`Hello, I'm ${this.name}`);
}
}
const p1 = new Person('Alice', 25);
✅ 本质 :
class
是上述组合模式的语法糖,但更清晰、更安全。
九、总结:选择哪种方式?
方式 | 类型识别 | 内存效率 | 初始化 | 封装性 | 推荐度 |
---|---|---|---|---|---|
字面量 | ❌ | ⚠️ | ✅ | ✅ | ⭐⭐ |
工厂模式 | ❌ | ❌ | ✅ | ✅ | ⭐⭐⭐ |
构造函数 | ✅ | ❌ | ✅ | ✅ | ⭐⭐⭐ |
原型模式 | ✅ | ✅ | ❌ | ⚠️ | ⭐⭐ |
组合模式 | ✅ | ✅ | ✅ | ⚠️ | ⭐⭐⭐⭐⭐ |
动态原型 | ✅ | ✅ | ✅ | ✅ | ⭐⭐⭐⭐ |
寄生构造 | ❌ | ✅ | ✅ | ✅ | ⭐⭐⭐ |
✅ 最终建议:
- 现代开发 :使用
class
;- ES5 环境 :使用组合模式;
- 特殊需求:根据场景选择工厂、寄生等模式。
💡 结语
"理解对象创建方式,是掌握 JavaScript 面向对象编程的基石。"
从字面量到 class
,JavaScript 的对象创建方式不断演进,核心目标始终是:
- 代码复用
- 内存优化
- 类型识别
- 开发效率
掌握这些模式,你就能写出更健壮、更可维护的代码。