原型模式
什么是原型模式?
Prototype(原型)模式 是一种 创建型设计模式。
其核心思想是通过 复制现有对象(原型对象)来创建新对象,而不是通过类实例化。 在 JavaScript 中,由于语言本身的 基于原型的继承机制,原型模式天然地与对象的行为和继承紧密结合。
提示
原型模式在 JavaScript 中既是设计模式,也是语言核心机制。 它适合高频创建对象和动态扩展的场景,但需警惕共享状态的副作用。 合理利用原型模式,可以显著提升代码复用性和性能,但需结合项目需求权衡是否引入复杂性。
实现原型模式
- 原型链继承:每个 JavaScript 对象都有一个
[[Prototype]]
(可通过__proto__
或Object.getPrototypeOf()
访问),指向其原型对象。 - 共享属性和方法:所有实例共享原型对象上的属性和方法,减少重复定义。
- 动态修改:修改原型对象的属性和方法,会立即反映到所有实例上。
// 原型对象定义
const carPrototype = {
wheels: 4,
drive() {
console.log('Driving...')
},
}
// 通过 Object.create() 创建新对象(基于原型)
const myCar = Object.create(carPrototype)
myCar.color = 'red'
console.log(myCar.wheels) // 4(继承自原型)
myCar.drive() // "Driving..."
优点
高效内存利用
原型上的方法和属性被所有实例共享,避免重复创建相同功能的方法,节省内存。
动态性和灵活性
运行时可以动态修改原型,所有实例自动继承变更。
避免重复代码
通过复用原型,减少冗余代码(例如构造函数中重复绑定方法)。
天然支持 JavaScript 继承
与 JavaScript 的原型链机制无缝结合,是语言原生特性的直接应用。
缺点
共享状态的风险
如果原型属性是引用类型(如数组、对象),所有实例会共享同一份数据。
原型链复杂性
深层次的原型链可能导致属性查找性能下降(需逐级向上搜索)。
多重继承或复杂原型链可能使代码难以维护。
覆盖问题
实例可以覆盖原型属性,可能导致意外行为:
const c = Object.create(carPrototype) c.wheels = 3 // 覆盖原型属性 console.log(c.wheels) // 3(实例属性优先于原型)
适用场景
需要创建大量相似对象
例如游戏中的 NPC 敌人、粒子系统等高频创建的场景,复用原型节省内存。
动态扩展对象功能
在运行时为所有实例添加/修改功能(如日志功能、监控逻辑)。
替代类继承
当类继承导致复杂层次结构时,原型模式更轻量灵活。
与
Object.create()
结合使用明确指定原型对象,避免构造函数和 new 关键字的限制。
最佳实践
- 避免共享引用类型属性:将引用类型属性定义在构造函数中,而非原型。
- 浅拷贝/深拷贝:需要完全独立对象时,使用
Object.assign()
或深拷贝工具。 - ES6 Class 语法糖:底层仍基于原型,但提供更清晰的抽象。