Proxy;Reflect;捕获trap;捕获器不变式;代理的应用;

疑问:

  • 代理到底有什么用?
    • 开源库中应该用得更多,日常开发的应用较少,适合做各种数据的批量监控与管理。
  • 代理和C++指针的区别是?
  • 什么是一等函数?

    心得&书摘:

    1 代理基础

    定义

  • 代理是目标对象的抽象

  • 类似C++的指针
    • 既可以当成对象的替身,又完全独立于对象
    • 然而还是存在重大区别的
  • 代理是使用Proxy构造函数创建的

    1. const proxy = new Proxy(target, handler);
  • 在代理对象上执行的任何操作实际上都会应用到目标对象

  • 可撤销代理

    • 普通代理与代理对象的联系会在代理对象的生命周期内一直持续存在
    • revocable()方法
      • 支持撤销代理对象与目标对象的关联(不可逆)

        捕获

  • 使用代理的主要目的是可以定义捕获器(trap)

    • 原来英文是trap,难怪有的资料翻译为陷阱
    • 基本操作拦截器
    • image.png
  • 当通过代理对象执行get()操作时,就会触发定义的get()捕获器,从而在中间执行各种操作,甚至改变返回值。image.png
  • 但是,捕获处理程序的行为必须遵循“捕获器不变式”(trap invariant)

    • 简而言之就是不能变的太多,为所欲为,比如把不可变的属性改成可变的。
    • 不同的代理捕获器的捕获器不变式不同,使用时可以单独查看。

      重建

  • 所有捕获器都可以基于自己的参数重建原始操作,但并非所有捕获器行为都像get()那么简单。因此,通过手动写码如法炮制的想法是不现实的。

  • 调用全局Reflect对象上(封装了原始行为)的同名方法来轻松重建。
  • 各种花式重建 ```javascript // 老老实实 const handler = { get() { return Reflect.get(…arguments); } };

// 没啥意思 const handler = { get: Reflect.get };

// 何必? const proxy = new Proxy(target, Reflect); ```

Reflect

  • 最常用的地方就是上述的代理重建。
  • 相比对象API,某些情况下应该优先使用反射API

    • 标记状态:以下反射API会告知操作成功与否
      • ❑ Reflect.defineProperty()
      • ❑ Reflect.preventExtensions()
      • ❑ Reflect.setPrototypeOf()
      • ❑ Reflect.set()
      • ❑ Reflect.deleteProperty()
    • 代替一等函数:以下反射方法提供只有通过操作符才能完成的操作
      • ❑ Reflect.get():可以替代对象属性访问操作符。
      • ❑ Reflect.set():可以替代=赋值操作符。
      • ❑ Reflect.has():可以替代in操作符或with()。
      • ❑ Reflect.deleteProperty():可以替代delete操作符。
      • ❑ Reflect.construct():可以替代new操作符。
    • 安全地应用函数
      • 在通过apply方法调用函数时,被调用的函数可能也定义了自己的apply属性
      • Reflect.apply(myFunc, thisVal, argumentsList)
    • 代理另一个代理
      • 就是上述重建应用的扩展

        代理的问题

  • this指向

    • image.png
    • 书中与weakMap联合讲解了一下,过于深奥不做研究【四?】
  • 代理与内部槽位

    • 主要体现在于Date类型的协作上
    • image.png

      2 代理捕获器与反射方法

  • get()

    • get()捕获器会在获取属性值的操作中被调用。
    • 对应的反射API方法为Reflect.get()。
    • 返回值:无限制
    • 拦截的操作:
      • ❑ proxy.property
      • ❑ proxy[property]
      • ❑ Object.create(proxy)[property]
      • ❑ Reflect.get(proxy, property, receiver)
  • set()
    • set()捕获器会在设置属性值的操作中被调用。
    • 对应的反射API方法为Reflect.set()。
    • 返回值:布尔值
    • 拦截的操作:
      • ❑ proxy.property = value
      • ❑ proxy[property] = value
      • ❑ Object.create(proxy)[property] = value
      • ❑ Reflect.set(proxy, property, value, receiver)
  • has()
    • has()捕获器会在in操作符中被调用。
    • 对应的反射API方法为Reflect.has()。
    • 返回值:布尔值,属性是否存在
    • 拦截的操作
      • ❑ property in proxy
      • ❑ property in Object.create(proxy)
      • ❑ with(proxy) {(property); }
      • ❑ Reflect.has(proxy, property)
  • defineProperty()
    • defineProperty()捕获器会在Object.defineProperty()中被调用。
    • 对应的反射API方法为Reflect.defineProperty()。
    • 返回值:布尔值
    • 拦截的操作
      • ❑ Object.defineProperty(proxy, property, descriptor)
      • ❑ Reflect.defineProperty(proxy, property, descriptor)
  • ……更多详见书本

    3 代理模式——代理的应用

    1、跟踪属性访问

  • 监控对象

    • image.png
    • 通过捕获get、set和has等操作,可以知道对象属性什么时候被访问、被查询。

      2、隐藏属性

      image.png

      3、属性验证

      可以根据所赋的值决定是允许还是拒绝赋值
      image.png

      4、函数参数验证

      可以让函数只接收某种类型的值
      image.png

      5、数据绑定与可观察对象

      通过代理可以把运行时中原本不相关的部分联系到一起:
  • 通过定义一个统一的构造函数,实现在创建代理时将创建的对象收集起来

  • 每次插入新实例时都会发送消息

    6、做对象属性的批量修改,无视属性名

    比如批量修改接口返回值数据,可以做到不关心具体属性名,对对象的所有属性做统一处理:
    摘自阮一峰《ES6 入门教程》:https://es6.ruanyifeng.com/
    image.png

    7、为所欲为

    **第9章 代理与反射 - 图10