Proxy代理器

基本用法

  • ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例
    • var proxy = new Proxy(target, handler)
      • target参数表示所要拦截的目标对象
      • handler参数也是一个对象,用来定制拦截行为
      • new Proxy()之后返回一个对象,可以通过该对象,对target进行操作
  • 个人理解:我们来做个类比,A相当于targetB相当于proxy

    • A代理给B,然后提前先跟B,签合同handler,告诉B应该怎么做
    • 之后对A操作,就通过B来执行
      1. // 案例一:如果handler没有设置任何拦截,那就等同于直接通向原对象
      2. var target = {};
      3. var handler = {};
      4. var proxy = new Proxy(target, handler);
      5. proxy.a = 'b';
      6. target.a // "b"

      handler中的13种拦截方法

  • get(target, propKey, receiver)

    • 作用:拦截对象属性读取。比如proxy.foo 和 proxy['foo']
    • 参数
      • target:传进Proxy构造函数的目标对象
      • propKey:读取某个属性时的属性名
      • receiver:执行Proxy构造函数后,返回的实例
  • set(target, propKey, value, receiver)
    • 作用:拦截对象属性的设置。比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值
    • 参数
      • value:读书某个属性时的属性值
  • has(target, propKey)
    • 作用:拦截propKey in proxy的操作,返回一个布尔值
  • deleteProperty(target, propKey)
    • 作用:拦截delete proxy[propKey]的操作,返回一个布尔值
  • ownKeys(target)
    • 作用:拦截Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy)for...in循环,返回一个数组
    • 该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性
  • getOwnPropertyDescriptor(target, propKey)
    • 作用:拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象
  • defineProperty(target, propKey, propDesc)
    • 作用:拦截Object.defineProperty(proxy, propKey, propDesc)Object.defineProperties(proxy, propDescs),返回一个布尔值
  • 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) ```javascript // 案例一 const obj = { age: 18 }; const handler = { get: function (target, propKey, receiver) { return target[propKey] }, set: function (target, propKey, value, receiver) { // 可以在方法上做一些处理,比如阻止修改原型 if (propKey === ‘proto‘) { return false; } target[propKey] = value; return true; }, has: function (target, propKey) { return target[propKey] !== undefined }, deleteProperty: function (target, propKey) { if (propKey === ‘proto‘) { return false; } delete target[propKey]; return true; }, getOwnPropertyDescriptor: function (target, propKey) { return Object.getOwnPropertyDescriptor(target, propKey); }, defineProperty: function (target, propKey, propDesc) { // 外部调用Object.defineProperties(proxy, propDescs),会逐次的调用这个函数 Object.defineProperty(target, propKey, propDesc); return true; }, preventExtensions: function (target) { // 即使不传入target,也要调用Object.preventExtensions()。不然在外部调用,会报错 Object.preventExtensions(); return true }, getPropertypeOf: function (target) { return Object.getPrototypeOf(target); }, isExtensible: function (target) { return Object.isExtensible(target) }, setPropertypeOf: function (target, proto) { Object.setPrototypeOf(target, proto); return true } }

const proxy = new Proxy(obj, handler);

  1. <a name="wsUyk"></a>
  2. ## 回收Proxy代理
  3. - `Proxy.revocable(target, handle)`
  4. - 作用:返回一个可取消的 Proxy 对象,该对象的`proxy`属性是`Proxy实例`,`revoke`属性是一个函数,可以取消`Proxy`实例
  5. - 当执行`revoke函数`之后,再访问`Proxy实例`,就会抛出一个错误
  6. ```javascript
  7. let target = {};
  8. let handler = {};
  9. let {proxy, revoke} = Proxy.revocable(target, handler);
  10. proxy.foo = 123;
  11. proxy.foo // 123
  12. revoke();
  13. proxy.foo // TypeError: Revoked

this问题

  1. // 案例一:this隐式绑定
  2. const target = {
  3. m: function () {
  4. console.log(this === proxy);
  5. }
  6. };
  7. const handler = {};
  8. const proxy = new Proxy(target, handler);
  9. target.m() // false
  10. proxy.m() // true
  1. // 案例二:this由于隐式绑定,而产生的问题
  2. let set = new WeakMap();
  3. let target = {
  4. get value() {
  5. return set.get(this);
  6. },
  7. set value(val) {
  8. set.set(this, val)
  9. }
  10. }
  11. target.value = 456;
  12. let proxy = new Proxy(target, {});
  13. console.log(target.value); // 456
  14. console.log(proxy.value); // undefined
  1. // 案例三
  2. // 有些原生对象的内部属性,只有通过正确的this才能拿到,所以 Proxy 也无法代理这些原生对象的属性
  3. const target = new Date();
  4. const handler = {};
  5. const proxy = new Proxy(target, handler);
  6. proxy.getDate(); // TypeError: this is not a Date object.

proxy 的应用

  1. // 案例一:用proxy实现双向绑定
  2. const data = { text: '' };
  3. const input = document.querySelector('input');
  4. const p = document.querySelector('p');
  5. const handler = {
  6. set(target, key, value) {
  7. target[key] = value;
  8. // 数据变化 ——> 视图变化
  9. input.value = value;
  10. p.innerText = value;
  11. return true;
  12. }
  13. }
  14. const proxy = new Proxy(data, handler);
  15. input.onchange = function (e) {
  16. // 视图变化 ——> 数据变化
  17. proxy.text = e.target.value;
  18. }