本文档介绍了 JavaScript 中的委托模式,这是一种不同于传统类继承的设计方法,它利用了 JavaScript 的原型特性。委托模式利用对象链接和 this 上下文共享来实现灵活的对象组合和行为共享,而无需传统继承中的僵化层级。
虽然《Classy Objects》一章对类进行了广泛的介绍,但本页将专门关注理解和实现委托作为一种利用 JavaScript 自身的原型系统和动态 this 绑定机制的设计模式。
与传统的基于类的继承相比,委托代表了我们思考代码组织方式的根本性转变。委托不通过类层级创建父子关系,而是强调通过上下文共享进行协作的同级对象关系。
| 基于类的方法 | 基于委托的方法 |
|---|---|
| 垂直继承层级 | 水平对象关系 |
| 父子关系 | 同级对象协作 |
| 分类和特化 | 对象组合和委托 |
| 从父类继承的行为 | 委托给链接对象以获得行为 |
| 实例继承所有类行为 | 对象只委托特定行为 |
| 在编写时定义的固定关系 | 可在运行时更改的灵活关系 |
来源:objects-classes/ch5.md254-265 objects-classes/ch5.md266-317
在委托模式中,同级对象通过协作共享行为,而不是通过固定的层级继承。对象会将任务委托给其他包含所需特定行为的对象。
来源:objects-classes/ch5.md268-304
委托通过利用两个关键的 JavaScript 机制来实现:
这些机制实现了作者所说的“虚拟组合”——通过委托在运行时组合对象,而不是通过继承在编写时组合。
来源:objects-classes/ch5.md306-317 objects-classes/ch4.md191-222
主要有两种实现委托的方法:
使用 .call() 或 .apply() 来显式地在对象之间共享上下文。
这种方法在一个对象(ObjectB)上调用一个方法,但使用另一个对象(ObjectA)的上下文(this)。它被认为是“显式”的,因为委托在每个调用点都可见。
来源:objects-classes/ch5.md298-299 objects-classes/ch4.md202-222
使用 [[Prototype]] 链自动委托属性/方法查找。
这种方法依赖于 JavaScript 引擎在 ObjectA 上查找 someMethod,如果找不到,则继续在原型链上向上搜索到 ObjectB。
来源:objects-classes/ch5.md300-304 objects-classes/ch2.md268-298
在委托模式中,对象的关系与类层级中的关系不同。
这种模型强调
来源:objects-classes/ch5.md323-334 objects-classes/ch5.md376-436
以下存储库中的示例展示了多个对象如何相互委托行为。
这说明了显式委托(使用 .call())和隐式委托(使用原型链接)。
来源:objects-classes/ch5.md376-400 objects-classes/ch5.md435-501
委托最强大的方面之一是“虚拟组合”——即能够创建看起来组合在一起但实际上存在于不同对象中的行为。
通过委托实现的虚拟组合允许
来源:objects-classes/ch5.md316-317 objects-classes/ch5.md426-436
委托模式提供了多种优势:
来源:objects-classes/ch5.md241-253 objects-classes/ch5.md497-502
为了更清楚地理解委托模式,将两种方法实现相同功能进行比较会很有帮助。
委托方法
来源:objects-classes/ch5.md267-317 objects-classes/ch3.md91-105
当出现以下情况时,委托模式特别有用:
但是,需要认识到委托模式并不是 JavaScript 中的主流方法。作者承认,您不会发现许多大型应用程序使用这种模式,因为大多数现代 JavaScript 代码倾向于使用基于类的模式。
来源: objects-classes/ch5.md15-26 objects-classes/ch5.md582-591
委托模式介于类继承和模块/闭包模式的代码组织方法之间。以下是它们的对比:
| 模式 | 基于 | 组合方法 | 状态/行为关系 |
|---|---|---|---|
| 面向类 | 继承层级 | 垂直继承 | 类中紧密耦合 |
| 委托 | 对象链接 & 上下文 | 水平委托 | 分离为对等对象 |
| 模块/工厂 | 闭包 & 函数作用域 | 显式组合 | 封装在闭包中 |
来源: objects-classes/ch5.md24-28 objects-classes/ch5.md59-96
在实现委托模式时,请考虑以下实践:
Object.create() 来更清晰地链接原型,而不是使用 __proto__。来源: objects-classes/ch5.md229-244 objects-classes/ch2.md344-362
委托模式代表了一种不同的 JavaScript 代码组织思维方式——它拥抱了语言的寄生(prototypal)特性,而不是试图将其强加于类继承之上。虽然不如类继承方法那样主流,但委托模式提供了灵活性、模块化,并且能更直接地利用 JavaScript 的核心机制。
通过理解委托模式,您将获得 JavaScript 编程工具箱中又一个强大的工具,即使您在日常实践中主要使用基于类别的代码。