JavaScript 中"对象即函数"设计模式

概念介绍

"对象即函数"设计模式是一种在 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 灵活性思维的体现。 在合适的场景下应用这种模式,能为我们的代码带来更多的可能性和更好的用户体验。

相关推荐
小lan猫1 小时前
React学习笔记(一)
前端·react.js
拜无忧1 小时前
【教程】Nuxt v4 入门指南与实践 (vue前端角度开发)
前端·nuxt.js
云枫晖2 小时前
手写Promise-什么是Promise
前端·javascript
拜无忧2 小时前
html,svg,花海扩散效果
前端·css·svg
DevUI团队2 小时前
🚀 MateChat V1.8.0 震撼发布!对话卡片可视化升级,对话体验全面进化~
前端·vue.js·人工智能
RoyLin2 小时前
TypeScript设计模式:责任链模式
前端·后端·typescript
一枚前端小能手2 小时前
📋 前端复制那点事 - 5个实用技巧让你的复制功能更完美
前端·javascript
三小河2 小时前
解决vite环境下调用获取二进制文件流 部分文件报错 (failed)net::ERR_INVALID_HTTP_RESPONSE)
前端
好好好明天会更好2 小时前
pinia从定义到运用
前端·vue.js