call 是实现方法劫持最直接可控的手段,可显式绑定 this 和参数,支持前置/后置逻辑注入、避免 this 丢失,并能结合 object.defineproperty 实现无感劫持。

JavaScript 中 call 的显式绑定能力,是实现方法劫持(Method Hijacking)最直接、可控的手段之一。它允许你在不修改原对象定义的前提下,临时接管某个函数的执行上下文和参数,从而插入监控、增强、拦截或替换逻辑。
劫持目标方法并重定向 this
方法劫持的核心在于控制函数调用时的 this 值和实参。使用 call 可以精确指定被调用函数运行时的上下文,绕过原本的绑定规则。
- 若原方法依赖
this访问实例属性或方法,直接赋值给新变量会丢失上下文;call能强制绑定目标对象,确保逻辑正常执行 - 适合劫持那些未用箭头函数定义、且未被
bind固定过this的普通函数 - 例如:
const original = obj.method; obj.method = function(...args) { return original.call(this, ...args); };—— 这是最小侵入的代理模式起点
在劫持中注入前置/后置逻辑
借助 call,你可以在原方法执行前后插入自定义行为,比如日志、权限校验、性能统计或返回值处理。
- 前置逻辑:在
call调用前执行检查或准备,如if (!canExecute()) throw new Error('Denied'); - 后置逻辑:捕获返回值或异常,例如记录耗时:
const start = Date.now(); const ret = original.call(this, ...args); console.log(`took ${Date.now() - start}ms`); return ret; - 注意不要意外改变原函数的返回值类型或抛出未预期异常,否则可能破坏原有业务流
避免常见陷阱:this 丢失与多次劫持
显式绑定虽灵活,但容易因调用链或重复包装引发问题。
- 劫持后的方法若被再次赋值给其他变量(如事件回调),仍可能因调用方式导致
this丢失 —— 此时应在劫持封装中始终用call显式传入原始上下文,而非依赖调用位置 - 多次劫持同一方法时,需保存原始引用(通常只存一次),否则可能形成递归调用或覆盖前序劫持逻辑
- 对已用
bind绑定过的函数,call无法覆盖其内部固定的this—— 此类情况需改用apply或重新提取原始函数(如从构造函数原型上获取)
配合 Object.defineProperty 实现无感劫持
单纯替换方法属性易被发现或绕过;结合 Object.defineProperty 可隐藏劫持痕迹,让调用方完全无感。
- 将原方法设为 getter,每次访问都返回一个经
call封装的代理函数 - 或重写 setter,在设置新值时自动包装,保证后续所有调用都经过控制层
- 示例:
Object.defineProperty(obj, 'method', { get() { return function(...args) { /* 劫持逻辑 */ return original.call(obj, ...args); }; } });
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











