proxy无法直接拦截链式调用,但可通过proxy+惰性构建+终止触发模拟sql链式查询器:中间方法仅收集参数并返回同一proxy实例,exec/then/tostring等终结操作才真正执行或生成sql。

Proxy 本身无法直接拦截“串联方法调用”(如 db.users.where('age > 18').select('name,email'))的链式调用行为,因为 JavaScript 中的链式调用本质是连续返回同一个对象(或新对象),而 Proxy 只能拦截对某个目标对象的访问操作(如 get、call、apply),不能自动捕获“上一个方法返回值再调用下一个方法”这一整条调用链。
但我们可以用 Proxy + 惰性构建 + 终止触发 的组合策略,模拟出类似 SQL 的链式查表器。核心思路是:所有中间方法(where、select、orderBy 等)不立即执行,只收集参数并返回同一个 Proxy 实例;最终调用 exec()、run() 或触发 then/toString 等终结操作时,才真正解析并执行查询。
用 Proxy 包裹查询构建器,延迟执行
创建一个代理对象,让它在每次访问任意方法名时,都返回自身(保持链式),同时把调用信息(方法名、参数)存入内部队列:
- 目标对象是一个空对象(或带默认配置的对象),Proxy 的
get拦截器负责捕获所有方法访问 - 当访问到未定义的方法(如
where)时,不报错,而是返回一个闭包函数,该函数接收参数、记录操作,并返回代理本身 - 关键:让 Proxy 对象同时具备
then(支持 await)、toString(方便调试打印)、exec(显式执行)等多种终结入口
支持常见 SQL 风格操作的拦截逻辑
在 get 拦截器中识别常用关键词,生成对应的操作描述:
FastAPI + Flask 混合部署最佳实践,解决路由定义、API 代理等常见问题,适用于同时运行 FastAPI API 与 Flask 前端的场景。
-
where→ 记录条件表达式或对象,如{ age: { $gt: 18 } } -
select→ 记录字段列表,支持字符串('name, email')或数组(['name', 'email']) -
limit/offset→ 存为数字型元数据 -
join→ 存为关联表描述,含别名、条件等 - 遇到
exec、then、valueOf、toString时,触发实际查询或生成 SQL 字符串
终结操作触发真实执行或生成语句
利用 JavaScript 对象的隐式转换机制,在 Proxy 上实现多种终结方式:
- 定义
then方法(返回 Promise),使它可以await db.users.where(...).select(...) - 重写
toString,返回可读的 SQL 片段(用于调试或日志) - 提供
toSQL()或exec()显式方法,返回 Promise 或结果 - 内部维护一个操作栈(
this._steps = []),每次链式调用 push 一条指令,终结时遍历生成逻辑
注意边界与限制
这种模式不是真正的“拦截调用链”,而是巧妙利用 Proxy 的动态属性访问 + 函数返回 + 终结触发来模拟。需注意:
- 不能拦截
obj.method().method()中的“两次点号”的语法结构,只能拦截每次点号后对方法名的访问 - 如果用户写了
db.users.where().where(),需要设计去重或覆盖逻辑(如后者覆盖前者) - 类型提示(TypeScript)会丢失,需配合声明文件或泛型包装增强类型安全
- 避免意外触发(比如把代理对象 JSON.stringify,应屏蔽或抛出提示)
不复杂但容易忽略。










