Reflect.apply 是专为安全、明确执行函数调用设计的规范级工具,不依赖目标函数的 call/apply 方法,不隐式转换 this,严格按 [[Call]] 执行,thisArgument 原样传入,argumentsList 必须为真数组,错误提前暴露,无副作用。

Reflect.apply 是专为安全、明确地执行函数调用而设计的规范级工具,它不依赖目标函数是否拥有 call 或 apply 方法,也不做隐式 this 转换,天然适合用于上下文劫持场景——尤其是当你要确保函数在指定对象上运行、参数被完整传递、且行为完全可预测时。
为什么 Reflect.apply 更适合上下文劫持
传统 fn.call(ctx, ...args) 在非严格模式下会把 null/undefined this 自动转成全局对象,还可能受原型污染或自定义 apply 方法干扰;而 Reflect.apply 严格按规范执行原生 [[Call]],thisArgument 值原样传入,argumentsList 必须是真数组,错误提前暴露,无副作用。
- 不触发目标函数自定义的
apply方法,绕过不可控逻辑 - 对原始值(如
42、"str")作为this时,不自动装箱,保持语义一致 - 在 Proxy 的
applyhandler 中签名完全匹配,转发零损耗 - 类型检查更早:target 非函数直接抛
TypeError,不等到运行时才失败
实现上下文劫持的典型写法
所谓“劫持”,本质是拦截调用并重定向执行上下文。Reflect.apply 是这个过程的核心执行器,而非装饰器本身。
- 对单个函数封装:用闭包固定
thisArg和预处理参数,再通过Reflect.apply执行const bound = (fn, ctx, fixedArgs) => (...rest) => Reflect.apply(fn, ctx, [...fixedArgs, ...rest]); - 配合 Proxy 实现透明代理:
const proxied = new Proxy(originalFn, {<br> apply(target, thisArg, args) {<br> // 劫持逻辑:记录、校验、改写 this 或 args<br> const safeThis = thisArg ?? window;<br> return Reflect.apply(target, safeThis, args);<br> }<br>}); - 在沙箱或插件系统中,避免
eval或with,用Reflect.apply精确注入上下文对象
关键注意事项与避坑点
看似简单,但几个细节决定是否真正“安全”和“可控”:
-
argumentsList必须是 Array 实例:类数组(如arguments、NodeList)需先转数组,Array.from(arguments)或[...arguments]是标准做法 - 不要传
Set、Map或字符串——即使可迭代,也不符合规范要求,会报错 - 若目标函数是箭头函数或绑定函数(
fn.bind()),其this已固化,Reflect.apply 的thisArgument不起作用,需提前判断 - 在严格模式下,传
null或undefined作thisArgument,函数内this就是null/undefined,不会降级为全局对象
与 Proxy.apply 的协同实践
这是最常见也最稳健的上下文劫持组合。Proxy 拦截调用意图,Reflect.apply 承担干净执行。
- handler 中必须用
Reflect.apply(target, thisArg, args),不能写target.apply(thisArg, args)或target(...args),否则this丢失、参数展开异常 - 可叠加中间逻辑:参数校验、执行计时、上下文注入(如添加
__traceId)、错误兜底等 - 适用于监控第三方 API(如
fetch、console.log)、封装 SDK 方法、实现 AOP 式日志或权限控制

















