Proxy代理

Proxy代理

一、基本概念

Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。

1.语法

const p = new Proxy(target, handler)

target:要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
handler:一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。

2.方法

除了使用实例化的方式创建proxy,Proxy对象也为我们提供了方法进行创建

Proxy.revocable(target, handler);

target:要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
handler:一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。
返回值: {“proxy”: proxy, “revoke”: revoke} 返回一个包含了代理对象本身和它的撤销方法的可撤销 Proxy 对象。

  • proxy
    表示新生成的代理对象本身,和用一般方式 new Proxy(target, handler) 创建的代理对象没什么不同,只是它可以被撤销掉。
  • revoke
    撤销方法,调用的时候不需要加任何参数,就可以撤销掉和它一起生成的那个代理对象。

3.简单使用

  let obj = {
    name: 'lis',
    age: 21
  }
  let handle = {}
  let a
  let proxy = new Proxy(obj, handle)
  console.log(proxy.name); // lis
  obj.name = 'dwq'
  console.log(proxy.name); // dwq
  proxy.age = 2
  console.log(obj.age); // 2
  proxy.address = '广州';
  console.log(obj.address); // 广州

即使我们的handler不进行任何操作,代理也会将所有应用到它的操作转发到target对象上。同样对target对象的操作也会同步到proxy上。

二、handler 对象的方法

handler对象的方法有13个
handler.get():属性读取操作的捕捉器。
handler.set():属性设置操作的捕捉器。
handler.deleteProperty():delete 操作符的捕捉器。
handler.has():in 操作符的捕捉器。
handler.apply():函数调用操作的捕捉器。
handler.construct():new 操作符的捕捉器。
handler.defineProperty():Object.defineProperty 方法的捕捉器。
handler.getPrototypeOf():Object.getPrototypeOf 方法的捕捉器。
handler.setPrototypeOf():Object.setPrototypeOf 方法的捕捉器。
handler.isExtensible():Object.isExtensible 方法的捕捉器。
handler.preventExtensions():Object.preventExtensions 方法的捕捉器。
handler.getOwnPropertyDescriptor():Object.getOwnPropertyDescriptor 方法的捕捉器。
handler.ownKeys():Object.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols 方法的捕捉器。
这里只列举常用的几个,其他的可以参考 MDN

1.get()

handler.get() 方法用于拦截对象的读取属性操作。

(1)语法

let p = new Proxy(target, {
  get(target, property, receiver) {}
});

参数:

target:目标对象。
property:被获取的属性名。
receiver:Proxy或者继承Proxy的对象
返回值:get方法可以返回任何值,且返回值会作为读取prop属性得到的值。

(2)具体使用

我们可以在get中进行相关操作,比如对返回值进行修改等等

  let target = {
    name: 'lis'
  }
  let p = new Proxy(target, {
    get(target, prop) {
      console.log('get');
      return prop in target? target[prop]: 'no value in target'
    }
  });
  console.log(p.name); // lis
  console.log(p.age); // no value in target

2.set()

handler.set() 方法用于捕获对象的属性值设置操作。

(1)语法

let p = new Proxy(target, {
  set(target, property, value) {}
});

参数:

target:目标对象。
property:要设置的属性名。
value:要设置的属性值。
返回值:set方法可以返回任何值。

(2)具体使用

我们可以在set中进行相关操作,比如vue中在此对视图进行更新

  let target = {
    name: 'lis'
  }
  let p = new Proxy(target, {
    set(target, prop, value) {
      console.log('set');
      target[prop] = value
      return true
    }
  });
  p.name = 'dwq'
  console.log(p.name); // dwq
  p.age = 10
  console.log(p.age); // 10

3.deleteProperty()

handler.deleteProperty() 方法用于拦截对对象属性的 delete 操作。

(1)语法

let p = new Proxy(target, {
  deleteProperty(target, property) {}
});

参数:

target:目标对象。
property:要被删除的属性名。
返回值:deleteProperty 返回一个 Boolean 类型的值,表示了该属性是否被成功删除。

(2)具体使用

该方法会拦截以下操作:

  • 删除属性: delete proxy[foo] 和 delete proxy.foo
  • Reflect.deleteProperty()
  let target = {
    name: 'lis',
    age: 10,
    address: '广州'
  }
  let p = new Proxy(target, {
    deleteProperty(target, property) {
      console.log('deleteProperty');
      delete target[property]
      return true
    }
  });
  delete p.name
  console.log(p.name); // undefined
  delete p['age']
  console.log(p.age); // undefined
  Reflect.deleteProperty(p, 'address')
  console.log(p.address); // undefined

4.has()

handler.has() 方法是针对 in 操作符的代理方法。

(1)语法

let p = new Proxy(target, {
  has(target, property) {}
});

参数:

target:目标对象。
property:是否在target的属性名。
返回值:返回一个 Boolean 类型的值,表示了该属性是否在target中。

(2)具体使用

我们可以在has中进行相关操作

  let target = {
    name: 'lis',
    age: 10,
    address: '广州'
  }
  let p = new Proxy(target, {
    has(target, property) {
      console.log('has');
      if (property === 'name') {
        return false
      }
      return property in target
    }
  });
  console.log('name' in p); // false
  console.log('age' in p); // true
  console.log('address' in p); // true
  console.log('phone' in p); // false

5.apply()

handler.set() 方法用于捕获对象的属性值设置操作。

(1)语法

let p = new Proxy(target, {
  apply(target, thisArg, argumentsList) {}
});

参数:

target:目标对象。
thisArg:被调用时的上下文对象。
argumentsList:被调用时的参数数组。
返回值:apply方法可以返回任何值。

(2)具体使用

我们可以在apply中进行相关操作,替换对原函数的调用

  let target = function (name, age) {
    return name + '-' + age
  }
  let p = new Proxy(target, {
    apply(target, thisArg, arguments) {
      if (arguments[0] === 'lis') {
        return 'name error'
      }
      return target(...arguments);
    }
  });
  console.log(p('lis', 21)); // name error
  console.log(p('dwq', 23)); // dwq-23

其中第二个参数thisArg与原函数稍有不同。

  let target = function () {
    console.log(this)
  }
  let p = new Proxy(target, {
    apply(target, thisArg) {
      console.log(thisArg);
    }
  });
  let obj = {
    funcP: p,
    funcT: target,
  }
  p()
  target()
  obj.funcP()
  obj.funcT()

全局上下文调用时,原生函数this为Window,而proxy为undefined。这一点需要注意一下。
Proxy代理

6.construct()

handler.construct() 方法用于拦截new 操作符. 为了使new操作符在生成的Proxy对象上生效,new target 必须是有效的。

(1)语法

let p = new Proxy(target, {
  construct(target, argumentsList, newTarget) {}
});

参数:

target:目标函数。
argumentsList:被获取的属性名。
newTarget:最初被调用的构造函数代理,就上面的例子而言是p。
返回值:construct方法必须返回一个对象。

(2)具体使用

我们可以在construct中进行相关操作

  let targetFunc = function (name) {
    this.name = name
  }
  let p = new Proxy(targetFunc, {
    construct(target, arguments, newTarget) {
      console.log(newTarget);
      if (arguments[0] === 'lis') {
        return {
          name: 'error'
        }
      }
      return new target(...arguments)
    }
  });
  console.log(new p('lis'));
  console.log(new p('dwq'));

Proxy代理

上一篇:千里马Android Framework实战开发-binder通信之Messenger介绍


下一篇:Android中Handler的消息机制--同步屏障