JavaScript 对象创建两大模式:工厂函数与类(Factories & Classes)
·

本文讲解 JavaScript 对象创建两大模式:工厂函数、ES6 类,覆盖构造函数、原型、私有字段、继承、组合 vs 继承、最佳实践与面试高频考点。
一、开篇:为什么需要对象模板?
如何批量创建大量相似对象(游戏角色、用户、商品)而不重复复制代码?
// 工厂函数:每次调用返回新对象
function createPlayer(name) {
return {
name,
health: 100,
attack() {
return `${this.name} attacks!`
}
}
}
// 类:创建对象的蓝图
class Enemy {
constructor(name) {
this.name = name
this.health = 100
}
attack() {
return `${this.name} attacks!`
}
}
// 两者都能创建对象
const player = createPlayer("Alice")
const enemy = new Enemy("Goblin")
二、本文你将学到
- 工厂函数写法与原理
- 构造函数与
new关键字底层逻辑 - ES6 类语法与 “语法糖” 本质
- 私有字段
#、闭包私有、getter/setter、静态成员 extends继承与super- 工厂组合 vs 类继承
- 工厂与类的选型指南
三、手动创建对象的问题
- 代码大量重复
- 极易出错
- 难以维护
- 无法保证结构一致
- 方法重复拷贝,浪费内存
四、三种创建对象方式(流水线类比)
- 手动创建:手工雕刻 → 慢、不一致
- 工厂函数:装配线 → 传入参数 → 产出对象
- 类 / 构造函数:模具 / 蓝图 →
new批量生成
五、工厂函数(Factory Function)
普通函数,每次调用返回一个新对象,不需要 new。
基础工厂
function createPlayer(name) {
return {
name,
health: 100,
attack() { ... }
}
}
带配置对象
function createCharacter(config) {
const defaults = { health:100, level:1 }
const settings = { ...defaults, ...config }
return { ...settings, attack() {} }
}
闭包实现真正私有(最强特性)
function createBankAccount(owner, initial = 0) {
// 完全私有,外部绝对无法访问
let balance = initial
const history = []
return {
deposit(amount) {
balance += amount
history.push(...)
},
getBalance() { return balance }
}
}
✅ 真正封装,无法被篡改。
工厂可返回不同类型对象
function createWeapon(type) {
if (type === 'sword') return {...}
if (type === 'bow') return {...}
}
工厂适用场景
- 需要真正私有变量
- 不需要
instanceof判断 - 希望结构更灵活
- 偏好函数式编程
六、构造函数(Constructor)
配合 new 使用,命名首字母大写。
function Player(name) {
this.name = name
this.health = 100
}
new 关键字底层 4 步
- 创建空对象
{} - 将原型指向构造函数的
prototype - 构造函数内
this绑定到新对象 - 返回这个对象
方法挂载到原型(节省内存)
Player.prototype.attack = function() { ... }
所有实例共享同一个方法。
七、ES6 类(Class)
本质是构造函数 + 原型的语法糖。
class Player {
constructor(name) {
this.name = name
this.health = 100
}
attack() { ... } // 自动挂载到 prototype
}
类核心特性
1. 类字段(公有属性)
class Character {
level = 1
exp = 0
}
2. getter / setter
get isAlive() { return this.health > 0 }
set hp(val) { this.health = Math.max(0, val) }
3. 静态成员(属于类,不属于实例)
static createHero(name) { return new Player(name) }
4. 私有字段 #(真正私有)
class BankAccount {
#balance = 0
deposit(amount) { this.#balance += amount }
}
外部访问直接报语法错误。
私有字段 # vs 闭包私有
#:方法共享原型,内存高效,支持instanceof- 闭包:每个对象方法独立,更灵活,但内存开销稍大
八、继承:extends + super
class Warrior extends Player {
constructor(name) {
super(name) // 必须先调用父构造
this.armor = 20
}
takeDamage(amount) {
return super.takeDamage(amount - this.armor)
}
}
继承必须遵守
子类构造必须先调用 super() 才能用 this
九、组合优于继承(核心思想)
继承:is-a(狗是动物) 组合:has-a(鸭子拥有游泳能力)
const canSwim = (state) => ({ swim() {} })
const canFly = (state) => ({ fly() {} })
function createDuck(name) {
const state = { name }
return { ...canSwim(state), ...canFly(state) }
}
灵活、无层级枷锁、易复用、易测试。
十、工厂 vs 类 终极对比表
| 特点 | 工厂函数 | 类 |
|---|---|---|
| 语法 | 普通函数 | class |
new |
不需要 | 必须 |
instanceof |
不支持 | 支持 |
| 私有 | 闭包天然支持 | # 私有字段 |
| 内存 | 方法独立复制 | 方法共享原型 |
this 问题 |
可完全避开 | 必须处理 |
| 灵活性 | 极高 | 中等 |
| 面向风格 | 函数式 | OOP |
十一、常见 4 大错误
- 构造函数忘记
new→ 污染全局 / 严格模式报错 - 子类构造未调用
super()→ 无法使用this - 以为
_var是私有 → 只是约定,完全可访问 - 工厂误用
this→ 方法被提取时this丢失
十二、选型指南
- 用 类:需要
instanceof、清晰层级、团队熟悉 OOP、框架要求 - 用 工厂:需要灵活组合、强封装、函数式、避免
this问题
十三、经典面试题(全文高频)
1. 工厂函数与类的区别是什么?
- 工厂是普通函数,返回对象;不需要 new。
- 类必须用 new 创建实例,支持 instanceof。
- 工厂用闭包实现私有;类用 #私有字段。
- 工厂方法是实例独立的;类方法共享原型。
- 工厂更灵活;类更符合传统 OOP 习惯。
2. new 关键字底层做了什么?
- 创建空对象
{} - 把对象的
[[Prototype]]指向构造函数的prototype - 构造函数内部
this绑定到这个新对象 - 返回该对象(构造函数显式返回非 null 对象则除外)
3. JS 如何实现真正的私有?
两种标准方案:
- 类 #私有字段(语言级别强制)
- 工厂函数 闭包变量(作用域隔离)
_下划线只是约定,不是私有。
4. 什么时候用组合而不是继承?
- 无法用 “is-a” 描述时
- 需要混合多个行为(飞 + 游 + 走)
- 不想被层级绑定
- 希望低耦合、易测试
优先组合,避免继承。
5. 为什么说 ES6 class 是语法糖?
因为底层仍然是构造函数 + 原型继承,没有引入新的继承模型,只是写法更简洁。
十四、常见误区(必看)
误区 1:JS 的类 = Java 的类
错。 JS 类是原型继承的语法包装,不是静态类型的类体系。
误区 2:工厂函数不如类强大
错。 工厂可以做到类做不到的事:
- 动态返回不同结构
- 天然无 this 陷阱
- 更早支持真正私有
误区 3:_var 是私有
错。 这只是约定,外部依然可以读写。
误区 4:必须一律用类
错。 现代 JS(React/Vue/Svelte)大量使用函数式 + 工厂模式。
十五、深继承的问题(大猩猩 / 香蕉问题)
你想要一根香蕉,结果得到了: 大猩猩 + 丛林 + 整个热带雨林。
深层继承会带来:
- 层级复杂
- 耦合严重
- 改父类必影响所有子类
- 难以复用行为
解决方案:组合。
十六、工厂组合模式(完整版)
把能力拆成小函数,自由组装:
const canWalk = (state) => ({ walk() {} })
const canSwim = (state) => ({ swim() {} })
const canFly = (state) => ({ fly() {} })
function createDuck(name) {
const state = { name }
return { ...canWalk(state), ...canSwim(state), ...canFly(state) }
}
function createPenguin(name) {
const state = { name }
return { ...canWalk(state), ...canSwim(state) } // 不会飞
}
继承 vs 组合
- 继承:is-a,层级固定,紧耦合
- 组合:has-a,灵活拼装,松耦合
- 现代 JS 官方建议:优先组合
十七、知识测验(最终版)
Q1:new 的 4 步?
已在上文给出。
Q2:实例方法 vs 原型方法?
- 实例方法:每个对象一份,浪费内存。
- 原型方法:所有对象共享,高效。
Q3:# 私有 vs 闭包私有?
- #:语法强制、原型共享、支持 instanceof
- 闭包:作用域隔离、更灵活、方法不共享
Q4:super 作用?
调用父类构造函数、调用父类方法。 必须在 this 之前调用。
Q5:为什么用组合?
灵活、无层级枷锁、可混合任意行为、易维护。
Q6:class 为什么是语法糖?
底层仍然是构造函数 + 原型。
十八、常见问题 FAQ(完整版)
Q:工厂和类到底选哪个?
- 需要 instanceof / 清晰层级 / OOP → 类
- 需要 灵活 / 组合 / 真私有 / 函数式 → 工厂
Q:JS 类是真正的类吗?
不是。是基于原型的语法糖。
Q:类私有字段 # 有什么用?
提供语言级强制私有,外部无法访问。
Q:什么时候用工厂代替类?
- 不需要判断类型
- 希望返回不同结构
- 想避免 this
- 偏好函数式
Q:new 底层原理?
创建对象 → 绑定原型 → 绑定 this → 返回对象。
十九、相关概念(原文末尾)
- 对象创建与原型
- this、call、apply、bind
- 继承与多态
- 设计模式:工厂、单例、组合等
🧩 概念(完整版)
1. 创建模式
- Factory Function(工厂函数) 同类:对象工厂、函数式创建、闭包创建、无 new 创建
- Constructor(构造函数) 同类:实例构造、new 模式、原型构造
- ES6 Class(类) 同类:语法糖、OOP、原型面向对象、蓝图
2. 私有与封装
- Closure Private(闭包私有) 同类:函数作用域私有、真私有、封装
- # Private Field(类私有字段) 同类:硬私有、语言级私有、不可访问
- Underscore Convention(下划线约定) 同类:软私有、规范、非强制
3. 类体系核心
- constructor(构造器) 同类:初始化、实例构造
- static(静态成员) 同类:类方法、类属性、工具方法
- getter/setter(存取器) 同类:计算属性、属性拦截、校验
- extends(继承) 同类:派生、子类、is-a
- super 同类:父类调用、构造委托
4. 复用模式
- Composition(组合) 同类:行为混入、has-a、Mixin、灵活组装
- Prototype Chain(原型链) 同类:方法共享、原型继承、内存优化
5. 核心机制
- new Operator(new 运算符) 同类:实例化、对象构造、四步构造
- instanceof 同类:类型判断、原型检测
- Syntactic Sugar(语法糖) 同类:简洁写法、底层不变
6. 编程范式
- Encapsulation(封装) 同类:数据保护、接口隔离
- OOP(面向对象) 同类:类、继承、实例
- FP(函数式编程) 同类:工厂、纯函数、组合、无 this
7. 经典问题
- Gorilla-Banana Problem(大猩猩香蕉问题) 同类:深继承弊端、过度继承、耦合陷阱
- Favor Composition Over Inheritance(组合优于继承) 同类:现代设计原则、灵活复用、松耦合

更多推荐

所有评论(0)