proxy 的 construct 陷阱是劫持类实例化的正确方式,它拦截 new 调用、保持继承链完整,并支持在创建前后插入监控逻辑。

不能直接用 Proxy 劫持构造函数的“实例化过程”本身,但可以劫持类(即构造函数)本身,从而在每次 new X() 调用时插入监控逻辑——这是真正可行、无侵入、且符合 JavaScript 运行时机制的做法。
为什么 new Proxy(Class, { construct }) 是正确起点
construct 陷阱专为拦截 new 操作而设计,它会在执行 new Target(...args) 时触发,且能拿到 target(原始类)、args(构造参数)和 newTarget(实际被 new 的类,用于处理继承场景)。这正是切面监控的黄金入口。
- 它不修改类定义,也不要求你重写第三方库源码
- 它天然覆盖所有
new调用路径,包括动态eval('new '+ clsName)之外的所有静态调用 - 你可以在实例创建前记录参数、打点计时,在创建后注入代理、打标签或绑定生命周期钩子
如何代理一个第三方类(如 axios.Axios 或 Lodash 的某个类)
关键前提是:你必须能拿到该类的原始引用。常见方式有:
-
ESM 导入:若你项目中
import { Axios } from 'axios',可立即const monitoredAxios = new Proxy(Axios, handler) -
CDN 全局变量:如
window.Axios,先缓存再代理:const original = window.Axios; window.Axios = new Proxy(original, handler) -
CJS/UMD 模块:需在模块加载完成、类暴露后第一时间代理(例如在
require后立刻包装)
注意:不要代理整个 axios 对象(它是普通对象),要代理其内部的构造函数(如 axios.Axios 或默认导出的类)。
construct 陷阱中必须用 Reflect.construct
错误写法:return new target(...args) 或 target.apply(null, args) —— 这会丢失 new.target,破坏 instanceof 和继承链,且无法支持子类构造。
FastAPI + Flask 混合部署最佳实践,解决路由定义、API 代理等常见问题,适用于同时运行 FastAPI API 与 Flask 前端的场景。
正确写法是:
return Reflect.construct(target, args, newTarget)- 这样确保了原型链、
this绑定、new.target值全部与原行为一致 - 你只需在调用前后插入日志、性能采样、上下文注入(如 traceId)等逻辑
典型监控场景与代码片段
例如监控所有 new URL() 实例化:
const originalURL = window.URL;
window.URL = new Proxy(originalURL, {
construct(target, args, newTarget) {
console.log('[CONSTRUCT] new URL with:', args[0]);
const start = performance.now();
const instance = Reflect.construct(target, args, newTarget);
console.log('[CREATED] URL instance:', instance.href, `in ${(performance.now() - start).toFixed(2)}ms`);
return instance;
}
});
对类实例进一步增强?可在 construct 返回前,用 Proxy 再包一层实例对象,实现方法调用级监控(如拦截 instance.toString()),形成双层切面。
不复杂但容易忽略:construct 监控只生效于显式 new 调用;若第三方库内部用 Object.create(Class.prototype) 或工厂函数绕过 new,则需配合 get 陷阱监控其工厂方法——但那已不属于“构造函数劫持”范畴了。










