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;
Last Updated:
Contributors: zhangfei