概念介绍
"对象即函数"设计模式是一种在 JavaScript 中实现的特殊模式,它允许一个对象实例同时具备函数调用和对象方法调用的能力。这种模式的核心在于通过 JavaScript 的原型链(prototype chain)和函数绑定(function binding)机制,使一个对象实例在被调用时能够执行特定的方法。
在 unified 框架中,这种设计模式主要体现在 Processor 类上,它既可以通过 new Processor() 创建实例,也可以通过 processor() 函数式调用来创建新的处理器实例。
javascript
// 创建基础 unified 实例
export const unified = new Processor().freeze()
// 用户可以:
const processor = unified() // 调用 copy 方法,返回新的 Processor
.use(plugin) // 使用链式方法
.process(content) // 处理内容
核心原理分析
Processor 类是 unified 框架的核心组件,负责管理文档处理流程的各个阶段。Processor 类继承自 CallableInstance:
javascript
export const CallableInstance = function (property) {
// 第一阶段:获取原型信息
const self = this
const constr = self.constructor // 实例的构造函数
const proto = constr.prototype // 构造函数的原型对象
const value = proto[property] // 从原型中获取指定方法
// 第二阶段:创建可调用函数
const apply = function () {
// 将 this 绑定到 apply 函数本身
return value.apply(apply, arguments)
}
// 第三阶段:设置原型链
Object.setPrototypeOf(apply, proto)
return apply
}
而 Processor 类简化代码如下:
javascript
export class Processor extends CallableInstance {
constructor() {
// 如果 Processor() 被调用(没有 new),则调用 copy 方法
super('copy')
// ... 其他初始化代码
}
copy() {
// 创建新的处理器实例,复制所有配置
const destination = new Processor()
let index = -1
while (++index < this.attachers.length) {
const attacher = this.attachers[index]
destination.use(...attacher)
}
destination.data(extend(true, {}, this.namespace))
return destination
}
}
Processor 实例化时,调用 CallableInstance,实现对象即函数调用的特性。当调用实例时,会触发 copy 方法执行,返回一个新的 Processor 实例。
应用场景分析
"对象即函数"模式在统一入口、可组合性和可复用性方面具有显著优势。
场景一:工厂函数 + 配置方法
链式配置后,通过一次函数调用触发常用动作。
javascript
class DatabasePool extends CallableInstance {
constructor() {
super('copy') // pool() 将转发到 copy()
this.config = { max: 5, timeout: 1000 }
}
setMaxConnections(n) { this.config.max = n; return this }
copy() {
const next = new DatabasePool();
next.config = { ...this.config };
return next
}
}
const pool = new DatabasePool().setMaxConnections(10)
const isolated = pool() // 复制当前配置得到新实例
场景二:中间件系统
应用/路由对象既能装配中间件,又能作为 handler 被直接调用。
javascript
class Router extends CallableInstance {
constructor() {
super('dispatch') // app(req, res) 将转发到 dispatch()
this.stack = []
}
use(mw) { this.stack.push(mw); return this }
dispatch(req, res) {
let i = 0
const next = () => { const mw = this.stack[i++]; if (mw) mw(req, res, next) }
return next()
}
}
const app = new Router().use(authMiddleware)
// 可直接作为 http.createServer(app) 的 handler
场景三:流处理管道
装配变换步骤,调用即运行整个管道。
javascript
class DataPipeline extends CallableInstance {
constructor() { super('run'); this.steps = [] }
use(step) { this.steps.push(step); return this }
run(input) {
return this.steps.reduce((acc, fn) => fn(acc), input)
}
}
const pipeline = new DataPipeline()
.use(filterTransform)
.use(mapTransform)
const result = pipeline(rawData) // 调用即执行
实现方式对比
传统函数扩展方式
javascript
function createProcessor() {
const processor = {
use: function(plugin) {
// 添加插件
// 省略实现代码逻辑...
return this
},
process: function(content) {
// 处理内容
// 省略代码实现逻辑...
return result
}
}
// 返回函数版本
const callable = function() {
return createProcessor()
}
// 复制方法
Object.assign(callable, processor)
return callable
}
缺点:
- 每次创建都需要复制所有方法
- 原型链断裂,失去继承优势
- 内存使用效率低
Proxy 方式
javascript
function createCallableProxy(instance, methodName) {
return new Proxy(instance, {
apply(target, thisArg, argumentsList) {
return target[methodName].apply(target, argumentsList)
}
})
}
优缺点:
- ✅ 实现简洁
- ✅ 完全透明
- ❌ Proxy 在某些环境下可能不被支持
- ❌ 性能开销较大
- ❌ 调试复杂度增加
CallableInstance 方式对比
实现方式 | 性能 | 兼容性 | 调试友好度 | 内存效率 |
---|---|---|---|---|
传统函数扩展 | 中 | 优秀 | 中 | 低 |
Proxy | 低 | 良好 | 差 | 中 |
CallableInstance | 高 | 优秀 | 优秀 | 高 |
"对象即函数"不仅是一个设计模式,更是 JavaScript 灵活性思维的体现。 在合适的场景下应用这种模式,能为我们的代码带来更多的可能性和更好的用户体验。