【JavaScript】学习ES6中的代理反射Proxy

86 阅读1分钟

认识 Proxy 代理

Reflect: MDN文档链接

Proxy: MDN文档链接

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
    const obj = { a: 1, b: 2 }

      // 创建一个 Proxy 代理
      const proxy = new Proxy(obj, {
        // 设置属性值操作的捕获器
        set(target, property, value, receiver) {
          // console.log(target, properKey, value)
          // target[properKey] = value
          console.log('get', target, property, value, receiver)
          Reflect.set(target, property, value, receiver)
        },

        // 用于拦截对象的读取属性操作
        get(target, properKey, value, reveiver) {
          if (Reflect.has(target, properKey)) {
            // 如果读取的值存在 则返回
            return Reflect.get(target, properKey)
          } else {
            // 反之则返回 -1
            return -1
          }
        },

        // 手动修改 has 假设一直为 false
        has(target, properKye) {
          return false
        },
      })

      proxy.a = 10
      console.log('obj', obj, 'proxy', proxy)
      console.log('b' in proxy)
    </script>
  </body>
</html>

应用-观察者模式

使用Object.defineProperty实现

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="container"></div>

    <script>
      function observer(target) {
        const ob = {}
        const div = document.querySelector('#container')
        const props = Object.keys(target)

        for (const prop of props) {
          Object.defineProperty(ob, prop, {
            get() {
              return target[prop]
            },
            set(val) {
              target[prop] = val
              __render()
            },
            enumerable: true
          })
        }

        __render()

        function __render() {
          let html = ''
          for (const prop of Object.keys(ob)) {
            html += `
                <p>
                  ${prop}: ${ob[prop]}
                </p>
              `
          }
          div.innerHTML = html
        }

        return ob
      }

      const target = { name: '张三', age: 18 }

      const ob = observer(target)
    </script>
  </body>
</html>

使用Proxy实现

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="container"></div>

    <script>
      function observer(target) {
        const div = document.querySelector('#container')

        const proxy = new Proxy(target, {
          get(target, prop, value) {
            return Reflect.get(target, prop)
          },

          set(taget, prop, val) {
            Reflect.set(target, prop, val)
            __render()
          },
        })

        __render()

        // 渲染函数
        function __render() {
          let html = ''
          for (const prop of Object.keys(target)) {
            html += `
                <p>
                  ${prop}: ${target[prop]}
                </p>
              `
          }
          div.innerHTML = html
        }

        return proxy
      }

      const target = { name: '张三', age: 18 }

      const ob = observer(target)

      // 修改 ob 的属性时
    </script>
  </body>
</html>

应用-偷懒的构造函数

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      class User {
        // 通过 proxy 代理完成以下操作
        // constructor(firstname, lastname, age) {
        //   this.firstname = firstname
        //   this.lastname = lastname
        //   this.age = age
        // }
      }

      function ConstructorProxy(Class, ...propNames) {
        return new Proxy(Class, {
          construct(target, argumentList) {
            const obj = Reflect.construct(target, argumentList)

            // 循环统一赋值
            propNames.forEach((name, i) => (obj[name] = argumentList[i]))

            return obj
          },
        })
      }

      // 返回一个代理的构造函数
      const UserProxy = ConstructorProxy(User, 'firstname', 'lastname', 'age')

      const u = new UserProxy('张', '三', 18)
      console.log(u)
    </script>
  </body>
</html>

应用-可验证的函数参数

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      function sum(a, b) {
        return a + b
      }

      function validatorFun(fun, ...types) {
        return new Proxy(fun, {
          apply(target, thisArg, argArr) {
            types.forEach((type, index) => {
              if (typeof argArr[index] !== types[index]) {
                throw new TypeError(
                  `第 ${index + 1} 个参数 ${argArr[index]} 的类型不正确, 期待的类型为 ${types[index]}, 得到的类型则是 ${typeof argArr[index]}`
                )
              }
            })

            return Reflect.apply(target, thisArg, argArr)
          },
        })
      }

      const sumProxy = validatorFun(sum, 'number', 'number')

      console.log(sumProxy(1, 'a'))
    </script>
  </body>
</html>