Proxy 让我们可以对任何对象的绝大部分行为进行监听和干涉,实现更多的自定义程序行为。
用法:new Proxy(target, handler)。
   Proxy 通过设置行为监听方法来捕获程序对对应对象的行为。

    const obj = {};
    const proxy = new Proxy(obj, {
        // ...
    })

Proxy 的构造器接受两个参数,第一个参数为需要进行包装的目标对象,第二个参数则为用于监听目标对象行为的监听器,其中监听器可以接受一些参数以监听相对应的程序行为。
监听属性、参数及监听内容

属性值 监听器参数 监听内容
has (target, prop) 监听 in 语句的使用
get (target, prop, reciver) 监听目标对象的属性读取
set (target, prop, value, reciver) 监听目标对象的属性赋值
deleteProperty (target, prop) 监听 delete 语句对目标对象的删除属性行为
ownKeys (target) 监听 Object.getOwnPropertyName() 的读取
apply (target, thisArg, arguments) 监听目标函数(作为目标对象)的调用行为
construct (target, arguments, newTarget) 监听目标构造函数(作为目标对象)利用 new 而生成实例的行为
getPrototypeOf (target) 监听 Objext.getPrototypeOf() 的读取
setPrototypeOf (target, prototype) 监听 Objext.setPrototypeOf() 的调用
isExtensible (target) 监听 Objext.isExtensible() 的读取
preventExtensions (target) 监听 Objext.preventExtensions() 的读取
getOwnPropertyDescriptor (target, prop) 监听 Objext.getOwnPropertyDescriptor() 的调用
defineProperty (target, property, descriptor) 监听 Object.defineProperty() 的调用

has

可以通过为 Proxy 的 handler 定义 has 监听方法,来监听程序通过 in 语句来检查一个字符串或数字是否为该 Proxy 的目标对象中某个属性的属性键的过程。

const p = new Proxy({}, {
    has(target, prop){
        console.log(`Checking "${prop}" is in the target or not`);
        return true;
    }
})

console.log('foo' in p);
// Checking "foo" is in the target or not
// true
该监听方法有两个需要注意的地方,如果遇到这两种情况,便会抛出 TypeError 错误。

1.当目标对象被其他程序通过 Object.preventExtensions() 禁用了属性拓展 (该对象无法再增加新的属性,只能对当前已有的属性进行操作,包括读取、操作和删除,但是一旦删除就无法再定义) 功能,且被检查的属性键确实存在与目标对象中,该监听方法便不能返回 false。

const obj = {foo: 1};

Object.preventExtensions(obj);

const p = new Proxy(obj, {
    has(target, prop){
        console.log(`Checking "${prop}" is in the target or not`);
        return false; 
    }
})

console.log('foo' in p);   
//抛出Uncaught TypeError: 

2.当被检查的属性键存在与目标对象中,且该属性的 configurable 配置是 false 时,该监听方法不能返回 false。

const obj = {};

Object.defineProperty(obj, 'foo', {
    configurable: false,
    value: 10
})

const p = new Proxy(obj, {
    has(target, prop){
        console.log(`Checking "${prop}" is in the target or not`);
        return false;
    }
})

console.log('foo' in p);
//抛出Uncaught TypeError: 

get

Getter只能对已知的属性键进行监听,而无法对所有属性读取行为进行拦截,而 Proxy 可以通过设定 get 监听方法,拦截和干涉目标对象的所有属性读取行为。

const obj = {foo: 1};
const p = new Proxy(obj, {
    get(target, prop){
        console.log(`Program is trying to fetch the property "${prop}".`);
        return target[prop];
    }
})

alert(p.foo);  // Program is trying to fetch the property "foo".
alert(p.something);    // Program is trying to fetch the property "something".

  这个监听方法也存在需要注意的地方——当目标对象被读取属性的 configurable 和 writable 属性都为 false 时,监听方法最后返回的值必须与目标对象的原属性值一致。

const obj = {};

Object.defineProperty(obj, 'foo', {
    configurable: false,
    value: 10,
    writable: false
})

const p = new Proxy(obj, {
    get(target, prop){
        return 20;
    }
})

console.log(p.foo);

set

  handler.set 用于监听目标对象的所有属性赋值行为。注意,如果目标对象自身的某个属性是不可写也不可配置的,那么 set 不得改变这个属性的值,只能返回同样的值,否则报错。

const obj = {};
const p = new Proxy(obj, {
    set(target, prop, value){
        console.log(`Setting value "${value}" on the key "${prop}" in the target object`);
        target[prop] = value;
        return true;
    }
})

p.foo = 1;  
// Setting value "1" on the key "foo" in the target object

apply

handler.apply , Proxy 也为作为目标对象的函数提供了监听其调用行为的属性。

const sum = function(...args) {
  return args
    .map(Number)
    .filter(Boolean)
    .reduce((a, b) => a + b);

}

const p = new Proxy(sum, {
  apply(target, thisArg, args) {
    console.log(`Function is being called with arguments [${args.join()}] and context ${thisArg}`);
    return target.call(thisArg, ...args);
  }
})

console.log(p(1, 2, 3));
// Function is being called with arguments [1,2,3] and context undefined
// 6

construct

  handler.construct, Proxy 也可以将类作为目标监听对象,并监听其通过 new 语句来生产新实例的行为,这同样可以使用再作为构造器的构造函数上。

class Foo{};

const p = new Proxy(Foo, {
    construct(target, args, newTarget){
        return {arguments: args}    // 这里返回的结果会是 new 所得到的实例
    }
});

const obj = new p(1, 2, 3);
console.log(obj.arguments);  // [1, 2, 3]

创建可解除 Proxy 对象

用法:Proxy.revocable(target, handler) : (proxy, revoke)。

const obj = {foo: 10};
const revocable = Proxy.revocable(obj, {
    get(target, prop){
        return 20;
    }
})
const proxy = revocable.proxy;
console.log(proxy.foo); // 20
revocable.revoke();
console.log(proxy.foo); 
// TypeError: Cannot perform 'get' on a proxy that has been revoked

  Proxy.revocable(target, handler) 会返回一个带有两个属性的对象,其中一个 proxy 便是该函数所生成的可解除 Proxy 对象,而另一个 revoke 则是将刚才的 Proxy 对象解除的解除方法。


紫云梦
10 声望2 粉丝

« 上一篇
Reflect介绍
下一篇 »
OPN详解