ES6的Proxy ,一颗问路的石子

918 阅读2分钟

前言

let kind = false;
const person = new Proxy({}, {
    get (target, propKey, receiver) {
        if (propKey === 'kind') {
            return kind = !kind;
        }
        return Reflect.get(target, propKey, receiver)
    }
});
if (person.kind && !person.kind) {
    console.log('------');
}
// ------

果然成年人的世界不是非黑即白😭(远离毒鸡汤)。

简单介绍

  Proxy 是用于拦截对象访问的ES6特性。外界对代理实例的多数操作可在拦截器中捕获。

const proxy = new Proxy({}, {
    get(target, propKey, receiver) { },
    set(target, propKey, value, receiver) { },
    has(target, propKey) { },
    deleteProperty(target, propKey) { },
    ownKeys(target) { },
    getOwnPropertyDescriptor(target, propKey) { },
    defineProperty(target, propKey, propDesc) { },
    preventExtensions(target) { },
    getPrototypeOf(target) { },
    isExtensible(target) { },
    setPrototypeOf(target, proto) { },
    apply(target, object, args) { },
    construct(target, args) { }
});

  Proxy 支持拦截以上13种行为,详细的使用方法可以参考ES6入门-Proxy

  前言中的 Reflect ,可以理解为 Proxy 的『后门』,用于访问原始对象,同样支持13个方法,传参也与 Proxy 一致。

  Proxy 很厉害,看起来可以混淆正常的逻辑判断。

使用技巧

问路
if(input.show) {
	// input.show == true ?
} else {
	// input.show == false ?
}

  上面这段if判断在 input 是拦截器时会失效, show 可以在两次 get 给出不同的结果。如此可以穿透模块的逻辑分支,也就是投石问路的意思。输入端甚至可以通过捕获的内容输出一份日志。

const proxy = new Proxy({}, {
	get (target, key) {
      if (key === 'a') {
      	return 1;
      }
    }
});

console.log(JSON.parse(JSON.stringify(proxy))); // {}

不过,因为深拷贝时获取了代理的『快照』,拦截器在深拷贝后无法生效。不希望公开处理过程时,可以通过深拷贝解决。

isProxy

代理可以冒充成功是因为还无法区分拦截器和正常对象。不过拦截器状态需要对内部程序公开,经过拦截器的对象不需要再次包装拦截器。

const isProxy = Symbol();
const proxy = new Proxy({}, {
	get (target, key) {
      if (key === isProxy) {
      	return true;
      }
      return target[key];
    }
});

console.log(proxy[isProxy]); // true

使用Symbol作为判断拦截器占用的 key 可以避免无意识下与被代理对象冲突。

Proxy 与 defineProperty

ProxydefineProperty
作用于整个对象某个属性
生效场景访问属性访问属性
修改原始对象
IE兼容129
Proxy 不会替代 defineProperty,实际使用时按需求选择即可。