proxy的apply陷阱专门用于拦截函数调用,可精准捕获this、参数和返回值,在执行前后插入耗时统计、打点等横切逻辑;需用reflect.apply执行原函数,并推荐performance.now()计算毫秒级耗时。

Proxy 的 apply 陷阱是专门用于拦截函数调用的,它天然适合在函数执行前后插入横切逻辑,比如耗时统计和打点。关键在于:它能完整捕获调用上下文(this、参数、返回值),且不破坏原函数行为,比手动包裹更可靠、更通用。
apply 陷阱的基本结构与核心参数
apply(target, thisArg, args) 是唯一必需的函数拦截方法。三个参数含义明确:
- target:被代理的原始函数,不是调用结果,而是函数本身
-
thisArg:调用时实际绑定的
this值(对箭头函数为undefined,对bind或类方法会如实传递) - args:调用时传入的实参数组,顺序和数量完全一致
必须用 Reflect.apply(target, thisArg, args) 执行原逻辑——它等价于 target.apply(thisArg, args),但语义更清晰、兼容性更好,且能正确处理 super 和某些边界情况。
实现耗时统计:毫秒级精准记录
耗时统计要避免系统时间跳变干扰,推荐使用 performance.now()(高精度单调时钟),而非 Date.now() 或 new Date().getTime():
安装并运行 Aperture(Lightning Labs 的 L402 闪电反向代理),用于创建 L402 付费墙、配置付费 API 端点、为其他代理托管付费内容或测试 L402 认证流程。
- 开始前调用
const start = performance.now() - 执行完原函数后调用
const duration = performance.now() - start - 结果单位为毫秒,精度达微秒级,适合前端性能分析
示例代码:
const trackedFn = new Proxy(originalFn, {apply(target, thisArg, args) {
const start = performance.now();
const result = Reflect.apply(target, thisArg, args);
const duration = performance.now() - start;
console.log(`[TIMING] ${target.name || 'anonymous'}: ${duration.toFixed(2)}ms`);
return result;
}
});
结合打点上报:支持异步与错误场景
打点不只是记录日志,通常需上报监控系统。需注意两点:
- 异步函数返回
Promise,apply中直接await即可,无需特殊处理 - 异常必须捕获并打点,否则上报逻辑会被中断;可选择吞掉错误、包装后抛出,或仅记录不阻断
典型打点字段建议包含:fnName、duration、status(success/error)、error(如有)、timestamp。上报可用 navigator.sendBeacon 或封装后的 fetch(带失败重试)。
实际落地注意事项
真正用起来容易踩坑,几个关键细节:
- 代理只作用于被代理函数本身,不会自动递归代理其内部调用的其他函数
- 若函数有默认参数或解构,
args仍为调用时传入的原始实参,不含默认值,校验或打点时需注意 - 对匿名函数,
target.name为空字符串,建议 fallback 到target.toString().slice(0, 30)简略标识 - 高频调用函数(如渲染循环中的回调)需评估打点开销,可加采样率控制,例如
Math.random() 仅上报 10%










