proxy
proxy 与 Object.defineProperty 优劣对比
proxy
优势
- 可以直接监听对象而非属性
- 可以之间监听数组的变化
- 支持 13 种操作拦截
- 返回值是一个新对象,可以只操作新的对象达到目的
缺点:兼容性不好,无法通过 polyfill 模拟
Object.defineProperty
优势:兼容性好
缺点
- 无法监听数组的变化
- 监听的是对象的属性,当对象为多层嵌套的时候,必须递归遍历
proxy 使用
proxy 等同于在语言层面做出修改,属于一种“元编程”,即对编程语言进行编程
语法
var proxy = new Proxy(target, handler);
- target:要拦截的对象
- handler:定制拦截行为
示例
let obj = new Proxy(
{},
{
get: function (target, propKey, receiver) {
console.log(`getting ${propKey}`);
return Reflect.get(target, propKey, receiver);
},
set: function (target, propKey, value, receiver) {
console.log(`setting ${propKey}`);
return Reflect.set(target, propKey, value, receiver);
},
}
);
obj.count = 1;
++obj.count;
proxy 支持的操作
get(target, propKey, receiver):拦截对象属性读取set(target, propKey, value, receiver):拦截对象属性设置,返回一个布尔值has(target, propKey):拦截 in 操作,返回一个布尔值deleteProperty(target, propKey):拦截 delete 操作,返回一个布尔值ownKeys(target):拦截自身属性的读取,返回一个数组。拦截以下操作Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy)for...in循环:该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(),返回属性的描述对象defineProperty(target, propKey, propDesc):拦截Object.defineProperty()、Object.defineProperties(),返回一个布尔值preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。
如果目标对象是函数,那么还有两种额外操作可以拦截
apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)
Proxy.revocable()
返回一个可取消的 Proxy 实例
let { proxy, revoke } = Proxy.revocable(target, handler);
使用 revoke() 取消 Proxy 实例
Proxy.revocable() 的一个使用场景是,目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问。
this 问题
经过 Proxy 代理的情况下,目标对象内部的 this 会指向 Proxy 代理,即使不做任何拦截
const target = new Date('2020-01-01');
const handler = {
get(target, prop) {
if (prop === 'getDate') {
return target.getDate.bind(target);
}
return Reflect.get(target, prop);
},
};
const proxy = new Proxy(target, handler);
proxy.a;