【js篇】JavaScript 对象创建的 6 种方式:从基础到高级

在 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);

✅ 优点

  • 解决了代码重复问题;
  • 可通过参数初始化对象。

❌ 缺点

  • 无法识别对象类型 :

    js 复制代码
    console.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 的执行过程

  1. 创建一个新对象;
  2. 将构造函数的 prototype 赋值给新对象的 [[Prototype]];
  3. 将 this 指向新对象;
  4. 执行构造函数体;
  5. 如果函数没有返回对象,则返回新对象。

✅ 优点

  • 可通过 instanceof 识别类型:

    js 复制代码
    console.log(p1 instanceof Person); // ✅ true
  • 支持参数初始化。

❌ 缺点

  • 方法在每个实例中重复创建 ,浪费内存:

    js 复制代码
    console.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;

✅ 优点

  • 方法只在原型上创建一次,内存高效 :

    js 复制代码
    console.log(p1.greet === p2.greet); // ✅ true

❌ 缺点

  • 无法通过构造函数传参初始化;

  • 引用类型属性被所有实例共享 :

    js 复制代码
    Person.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 识别类型:

    js 复制代码
    console.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 的对象创建方式不断演进,核心目标始终是:

  • 代码复用
  • 内存优化
  • 类型识别
  • 开发效率

掌握这些模式,你就能写出更健壮、更可维护的代码。

相关推荐
程序员Sunday6 小时前
JavaScript 事件循环面试题,宏任务与微任务怎么执行|Sunday面试指南
开发语言·javascript·面试·校招·事件循环·程序员sunday
Setsuna_F_Seiei6 小时前
前端转型 Agent 开发 06 之 Agent Memory 记忆系统(让 Agent 更智能,更懂你)
前端·agent·ai编程
zhangzeyuaaa7 小时前
Ruby 多线程、GVL(GIL)与 Mutex 完全指南
开发语言·前端·ruby
默_笙7 小时前
🌊 向量库和 ES 都查不出"关系",我只好给奶茶建了一张人脉网
前端·javascript
优选资讯7 小时前
标签打印软件怎么对接 Excel 数据
前端·excel
思无邪668 小时前
用 AI 做 JS 逆向:从抓包到复现的完整方法论
开发语言·javascript·人工智能
一个风轻云淡8 小时前
GCC 和 GDB命令简单解读
java·linux·前端
u0111026759 小时前
图片处理接口如何统一错误信息 前端提示与后端错误码设计
前端·状态模式
福兮说10 小时前
浏览器里把 iPhone 的 HEIC 转成 JPG:Chrome 解不开、转完大了七成、拍摄时间全丢,六个坑实测
前端·javascript·图像处理·chrome·ios·iphone·heic
libokaifa10 小时前
把语音模型塞进手机:sherpa-onnx 在 Android 上的 ASR / TTS
前端