Skip to content

ES6 Proxy 简介

Published: at 16:56

最近用到 Proxy 的地方还是挺多的(具体关注之后的文章就知道了),这里来总结一下具体的用法。

BGM
BGM

ToC

何为 Proxy?

Proxy,简单来说,就是用来代理访问一个对象的那层代理。Proxy 可以拦截对其对应对象的几乎所有请求,包括赋值、取值、执行(函数调用)、类型判断(instanceof)等。这里直接给出 MDN 的地址吧,具体的介绍可以去那里看。

但是,虽说 Proxy 是透明的,但这种透明也只是相对的,对于 JavaScript 而言的透明,当遇到 Native Code 时,透明性就不再生效了。例如你如果 Proxy 了一个 DOM Element:

1
const element = document.createElement("a");
2
const proxy = new Proxy(element, {});
3
document.body.appendChild(proxy);

就会出现报错:

原因也很显而易见,DOM 不吃这个。类似的事情也会发生在 canvasdrawImage 之类的函数上,代理时须多多留意。

target

target 是 Proxy 代理的客体,其只要满足 typeof target === "object" 即可。target 在 Proxy 新建的时候传入,并且在每次 trap 触发时都会被传入对应的 handler 函数作为参数。

handler

handler 是声明了 Proxy 代理行为的对象。handler 为一个对象,其中的所有有效内容(指对 Proxy 而言有用的内容)均为函数。这里术语上将每一个不同的这样的函数称为 trap,如 getset 等。

handler.get

handler.get(target, property, receiver) 可以拦截该 Proxy 目前被要求获得的属性。

这里我们来设想一下,对于一个普通的对象 o,如果我们要访问它的某一个属性,会怎么访问呢?

最简单的就是 o[property] 了,这也是最常用的访问方式。但我们再考虑另外一个问题:如果 o[property] 是函数,会怎么样?

这个结论是很显然的,在某些情况下,会导致 this 的丢失。这里给出一个最简单粗暴的方法:

1
new Proxy(element, {
2
get: function (target, key) {
3
if (typeof target[key] === "function") {
4
return target[key].bind(target);
5
}
6
return target[key];
7
},
8
});

为什么说是简单粗暴呢?因为它直接将所有函数类型都和 target 强制绑定了。在大部分情况下,这样是不会出问题的,但如果使用了箭头函数的话,强行绑定 this 的逻辑就不对了。 // 这里还需要斟酌一下,如果有 dalao 发现问题还请指正(

handler.set

handler.set(target, property, value, receiver) 可以拦截该 Proxy 目前被要求设置的属性和值。这里的处理一般就很简单了,target[property] = value 就可以了,不过最后要返回 true 以表示成功。

handler.apply

handler.apply(target, _this, _arguments) 可以拦截该 Proxy 作为函数执行时的操作,并返回函数的返回值。通过 handler.apply,我们可以直接修改原函数的实现。

Practice:拦截 Raven

基础的知识就到这里,细节可以去 MDN 看。这里我们来看一个案例:为了减少调用栈的深度,方(fang)便(zhi)我们前端逆向的操(ka)作(si),对于启用了 Raven 的站点,我们需要将其拦截。

拦截的第一步是阻止脚本加载,这一步可以用多种方法实现,不论是 ABP,又或是开发者工具的 Block,相信都很好解决,这里就略过了。

拦截的第二步是伪造一个 Raven,因为当前目标代码中肯定用到了 Raven,因此需要伪造一个什么都不干的对象,但是可以被访问赋值调用

答案在此:

1
window.Raven = new Proxy(function () {}, {
2
get() {
3
return window.Raven;
4
},
5
apply() {
6
return window.Raven;
7
},
8
});

是不是很简单,很优雅(

结语

ES6 的 Proxy 给前端逆向对 Object 的控制注入了灵魂,之前虽然可以通过 Object 进行操作,但相比与现在的 API 还是过于粗糙了。

Proxy 可以实现的事情还有很多,比如 Sandbox(雀魂 Plus 1.x 就是使用的基于 Proxy 的沙箱)等。前端安全不容忽视啊(光速逃