本文讲解 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 类继承
  • 工厂与类的选型指南

三、手动创建对象的问题

  • 代码大量重复
  • 极易出错
  • 难以维护
  • 无法保证结构一致
  • 方法重复拷贝,浪费内存

四、三种创建对象方式(流水线类比)

  1. 手动创建:手工雕刻 → 慢、不一致
  2. 工厂函数:装配线 → 传入参数 → 产出对象
  3. 类 / 构造函数:模具 / 蓝图 → 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 步

  1. 创建空对象 {}
  2. 将原型指向构造函数的 prototype
  3. 构造函数内 this 绑定到新对象
  4. 返回这个对象

方法挂载到原型(节省内存)

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 大错误

  1. 构造函数忘记 new → 污染全局 / 严格模式报错
  2. 子类构造未调用 super() → 无法使用 this
  3. 以为 _var 是私有 → 只是约定,完全可访问
  4. 工厂误用 this → 方法被提取时 this 丢失

十二、选型指南

  • :需要 instanceof、清晰层级、团队熟悉 OOP、框架要求
  • 工厂:需要灵活组合、强封装、函数式、避免 this 问题

十三、经典面试题(全文高频)

1. 工厂函数与类的区别是什么?

  • 工厂是普通函数,返回对象;不需要 new
  • 类必须用 new 创建实例,支持 instanceof
  • 工厂用闭包实现私有;类用 #私有字段
  • 工厂方法是实例独立的;类方法共享原型
  • 工厂更灵活;类更符合传统 OOP 习惯。

2. new 关键字底层做了什么?

  1. 创建空对象 {}
  2. 把对象的 [[Prototype]] 指向构造函数的 prototype
  3. 构造函数内部 this 绑定到这个新对象
  4. 返回该对象(构造函数显式返回非 null 对象则除外)

3. JS 如何实现真正的私有?

两种标准方案:

  1. #私有字段(语言级别强制)
  2. 工厂函数 闭包变量(作用域隔离) _下划线 只是约定,不是私有。

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(组合优于继承) 同类:现代设计原则、灵活复用、松耦合

 

更多推荐