Proxy代理和反射

代理基础:

介绍:给目标对象定义一个关联的代理对象,而这个代理对象可以作为抽象的目标对象来使用。在对目标对象的各种操作影响目标对象之前,可以在代理对象中对这些操作加以控制。

用作目标对象的替身,但又完全独立于目标对象。目标对象既可以直接被操作,也可以通过代理来操作。

创建代理

代理是使用 Proxy 构造函数创建的。这个构造函数接收两个参数:目标对象和处理程序对象。缺 少其中任何一个参数都会抛出 TypeError。

默认情况下,在代理对象上执行的所有操作都会无障碍地传播到目标对象。

const target = { // ! 目标对象
            id: 'target'
        }
        const handler = {} // ! 处理程序对象
        const proxy = new Proxy(target, handler)
        
        // id 属性会访问同一个值
        console.log(target.id); // => target 
        console.log(proxy.id); // => target 

        // 给目标属性赋值会反映在两个对象上
        // 因为两个对象访问的是同一个值
        target.id = 'foo'; 
        console.log(target.id); // => foo 
        console.log(proxy.id); // => foo 

        // 给代理属性赋值会反映在两个对象上
        // 因为这个赋值会转移到目标对象
        proxy.id = 'bar'; 
        console.log(target.id); // => bar 
        console.log(proxy.id); // => bar 

console.log(target === proxy); // false

  • target:目标对象

  • handler:处理程序对象

定义捕获器

使用代理的主要目的就是定义捕获器,主要在处理程序对象中创建“拦截器”。

在操作系统中,捕获器是程序流中的一个同步中断,可以暂停程序流,转而执行一段子例程,之后再返回原始程序流。

定义一个 get()捕获器

const target = { // 目标对象
            id: 'target'
        }
        const handler = { // 处理程序对象
            get() {
                return target change!!!
            }
        }
        const proxy = new Proxy(target, handler)
        console.log(target.id) // => target
        console.log(proxy.id)  // => target change!!!

        console.log(target['foo']); // => target
        console.log(proxy['foo']); // => target change!!!

        console.log(Object.create(target)['foo']); // => target 
        console.log(Object.create(proxy)['foo']); // => target change!!!

捕获器在处理程序对象中以方法名为键

1.当通过代理对象执行 get()操作时,就会触发定义的 get()捕获器。
2.proxy[property]、proxy.property 或 Object.create(proxy)[property]等操作都 会触发基本的 get()操作以获取属性。
3.只要这些操作作用于代理对象上,就会触发 get()捕获,在目标对象上仍然是正常的行为

捕获器参数

get() 捕获器会接收到目标对象、要查询的属性和代理对象三个参数,于这些参数可以重建被捕获方法的原始行为。

const target = {
            id: 'target'
        }
        const handler = {
            get(IsTarget, property, receiver ) {
                console.log(IsTarget === target) // => true
                console.log(property) // => id
                console.log(receiver=== proxy) // => true
                
                /**
                 * 有了这些参数,我们可以重新处理捕获行为
                 */
                return IsTarget[property]
            }
        }
        const proxy = new Proxy(target, handler)
        console.log(proxy.id) // => target
        //执行结果
  • IsTarget:目标对象

  • property: 属性

  • receiver:代理对象

反射(Reflect)API 方法

捕获器都可以基于自己的参数重建原始操作,实际上、 开发者并不需要手动重建元素行为,而是可以通过调用全局 Reflect 对象(封装了原始行为)的同名方法重建。

const target = {
            id: 'target'
        }
        const handler = { 
            get: Reflect.get 
        }; 
        const proxy = new Proxy(target, handler)
        console.log(target.id) // => target
        console.log(proxy.id)  // => target

捕获器不变式

根据 ECMAScript 规范,每个 捕获的方法都知道目标对象上下文、捕获函数签名,而捕获处理程序的行为必须遵循“捕获器不变式” (trap invariant)。捕获器不变式因方法不同而异,但通常都会防止捕获器定义出现过于反常的行为。

如果目标对象有一个不可配置不可写的数据属性,那么捕获器会抛出TypeErroe

可撤销代理

Proxy.revocable()创建一个可撤销的Proxy对象。

作用:中断代理对象和目标对象之间的联系。(new Proxy() 创建的普通代理会在代理对象的什么周期一直持续存在)

const target = {
          id: 'target'
      }
      const handler = {
          get(IsTarget, property, receiver ) {
              return 'handler!!!'
          }
          // get: Reflect.get
      }
      // ! Proxy.revocable() 可撤销代理对象与目标对象的关联。
      const { proxy, revoke } = Proxy.revocable(target, handler)
      console.log(target.id) // => target
      console.log(proxy.id)  // => handler!!!
      revoke() // ! 撤销代理
      console.log(proxy.id) // => TypeError

这种操作是不可逆的

用一个代理去代理另一个代理

 target = {
            id: 'target'
        }
        const handler = {
            get(IsTarget, property, receiver ) {
                console.log('firstProxy')
                return Reflect.get(...arguments)
            }
        }
        const firstProxy = new Proxy(target, handler)

        const endProxy = new Proxy(firstProxy, {
            get(IsTarget, property, receiver ) {
                console.log('endProxy')
                return Reflect.get(...arguments)
            }
        })
        console.log(endProxy.id)
        // endProxy
        // firstProxy
        // target

get()

  • get()捕获器会在获取属性值的操作中被调用。对应的反射 API 方法为 Reflect.get()。

  • get() 返回值无限制,

  • target: 目标对象

  • property:目标对象的字符串键值

  • receiver:代理对象或者继承代理对象的对象

set()

set()捕获器会在设置属性值的操作中被调用。对应的反射 API 方法为 Reflect.set()。

const target = {}

        const proxy = new Proxy(target, {
            set(target, property, value, receiver) {
                console.log('set()')
                return Reflect.set(...arguments)
            }
        })
        console.log(proxy.name = '张三')

// set()
// 张三
  • target:目标对象。

  • property:引用的目标对象上的字符串键属性。

  • value:新属性值。

  • receiver:接收最初赋值的对象。

  • 返回值:返回 true 表示成功;返回 false 表示失败,严格模式下会抛出 TypeError

  • 如果 target.property 不可写且不可配置,则不能修改目标属性的值

has()

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

in: 如果指定的属性在指定的对象或其原型链中,则in 运算符返回true

const target = {}

       const proxy = new Proxy(target, {
           has(target, prop) {
               console.log('has()')
               return Reflect.has(...arguments)
           }
       })
       console.log('name' in proxy) // false
  • target:目标对象.

  • prop:需要检查是否存在的属性.

  • has has()必须返回布尔值,表示属性是否存在。

apply()

handler.apply() 方法用于拦截函数的调用。

function sum(a, b) {
        return a + b;
        }

        const handler = {
        apply: function(target, thisArg, argumentsList) {
            console.log(`apply ${argumentsList}`);
            return target(argumentsList[0], argumentsList[1]) * 10;
        }
        };

        const proxy = new Proxy(sum, handler);

        console.log(sum(1, 2)); // => 3
        console.log(proxy(1, 2)); // => 30
  • target:目标对象(函数)。

  • thisArg:被调用时的上下文对象。

  • argumentsList:被调用时的参数数组。

  • 返回值:apply方法可以返回任何值。

你可能感兴趣的:(Proxy代理和反射)