JavaScript 使用 Proxy 劫持任意函数执行

在实际使用中有一个奇怪的需求。我需要劫持XMLHttpRequest.prototype.open来监控全部的请求内容。

以往常用的劫持方法是这样的

const originalOpen = XMLHttpRequest.prototype.open;

XMLHttpRequest.prototype.open = function() {
// do something;
originalOpen.apply(this, arguments);
}

这样做确实是可以做到,但是存在一个问题:如果 XMLHttpRequest.prototype.open被第三者重新覆盖掉了,我们就没法监视方法的执行了。

很自然想到去监视这个“覆盖”操作,在被覆盖的时候往别人身上插点代码,但是研究了半天,想要实现这个有一点问题。

监控对象的属性被赋值,可以使用Proxy。但是XMLHttpRequest.prototypewritable: false, configurable: false的,覆盖它并无效果。

那么我们还有另一种方法就是使用 setter 来监视赋值操作。

我们能不能给XMLHttpRequest.prototype.open新建一个setter在不影响它原来值的情况下监视它被赋值呢,答案是不可以。我们需要先备份原值。


XMLHttpRequest.prototype._open = XMLHttpRequest.prototype.open;

为了防止自我调用无限循环,使用 _open 来存储原来的值。同时定义一个新的 getter 重定向对原 open 的调用。

监视到赋值操作之后就需要在盖上来的东西上挂点自己的东西,最终我们还是使用 Proxyhandler.apply来劫持函数的“执行”这一动作。handler.apply第一个参数是被执行的函数,第二个参数是callee,为不影响其原有功能,用Function.prototype.call来手动指定它的this,第三参数为调用 f 时传入的参数的数组。注意是数组……传进函数的时候注意要展开,坑了我好一会儿(因为是在实际应用环境下操作的,有点复杂不好调试)。


Object.defineProperty(XMLHttpRequest.prototype, 'open', {
get: function () {
return this._open;
},
set: function (f) {
this._open = new Proxy(f, {
apply: function(f, instance, fargs) {
listen.call(instance, ...fargs);
return f.call(instance, ...fargs)
}
});
}
});

最终我们得到类似于上面的代码。在不影响原来函数功能的情况下插入了一段自己的listen代码,我好了。

“JavaScript 使用 Proxy 劫持任意函数执行”的一个回复

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注