vue2 Object.defineProperty 和 vue3 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>
  proxy 和 Reflect
  proxy 代理
  Reflect 反射

  <script>
    // Reflect 反射将对象的基本操作暴露出来直接使用 get set has等
    // 在get中return target[key] 和 return Reflect.get(target, key) 的区别?
    // 案例如下info函数


    /**
     * Proxy 的13种监听方式
     * obj: 目标对象 
     * handle: 具体的操作 
     **/
    var obj = {
      name: '周杰伦',
      age: '18',
      phoneNumber: '1888888888',
      get info() {
        return `姓名:${this.name} 年龄:${this.age}`
      }
    }

    var newTarget = new Proxy(obj, {
      // handler.get() 方法用于拦截对象的读取属性操作。
      "get": function (oTarget, sKey, vValue) {
        console.log('get', oTarget, sKey, vValue)

        // 如果不通过Reflect反射进行返回,info里面的this指向的是”源对象“的数据,不是代理对象的数据,所以在获取的时候,不会触发代理打印
        return oTarget[sKey]

        // Reflect返回的是代理对象的属性,可以被代理对象监听到取值
        return Reflect.get(oTarget, sKey, vValue)
      },
      // handler.set() 方法是设置属性值操作的捕获器。
      "set": function (oTarget, sKey, vValue, receiver) {
        console.log('set', oTarget, sKey, vValue, receiver, oTarget.hasOwnProperty(sKey))

        // 当尝试设置一个新属性时,会进入这里
        if (!oTarget.hasOwnProperty(sKey)) {
          // 如果属性不存在,则添加一个新的属性
          oTarget[sKey] = `New property: ${sKey}`;
          console.log(`Property ${sKey} added.`);
        } else {
          // 设置属性的值
          oTarget[sKey] = vValue;
        }

        return true; // 返回true表示设置成功
      },
      // handler.has() 方法是针对 in 操作符的代理方法。
      "has": function (oTarget, sKey) {
        console.log('has', oTarget, sKey)
      },
      // handler.deleteProperty() 方法用于拦截对对象属性的 delete 操作
      "deleteProperty": function (oTarget, sKey) {
      },
      // handler.ownKeys() 方法用于拦截 Reflect.ownKeys().
      "ownKeys": function (oTarget, sKey) {
      },
      // handler.defineProperty() 用于拦截对象的 Object.defineProperty() 操作。
      "defineProperty": function (oTarget, sKey, oDesc) {
      },
      // 方法是 Object.getOwnPropertyDescriptor() 的钩子。
      "getOwnPropertyDescriptor": function (oTarget, sKey) {
      },
      // handler.apply() 方法用于拦截函数的调用。
      "apply": function (target, thisArg, argumentsList) { },
      // 是一个代理(Proxy)方法,当读取代理对象的原型时,该方法就会被调用。
      "getPrototypeOf"(target) { },
      // handler.construct() 方法用于拦截 new 操作符。为了使 new 操作符在生成的 Proxy 对象上生效,用于初始化代理的目标对象自身必须具有 [[Construct]] 内部方法(即 new target 必须是有效的)。
      "construct"(target, args) { },
      // handler.isExtensible() 方法用于拦截对对象的 Object.isExtensible()。
      "isExtensible"(target) { },
      // handler.preventExtensions() 方法用于设置对Object.preventExtensions()的拦截
      "preventExtensions"(target) { },
      // handler.setPrototypeOf() 方法主要用来拦截 Object.setPrototypeOf().
      "setPrototypeOf": function (target, prototype) { }
    });

    // 代理对象添加新属性,需要在set中特殊处理
    newTarget.a = 10

    // 某一个属性是否属于这个对象 in 
    console.log('a' in newTarget, newTarget)

    // 改变新添加的代理属性,可以被监听到
    newTarget.a = 15

    console.log(newTarget.info)


  </script>

  <script>
    // vue3使用代理Proxy案例
    var star = {
      name: '周杰伦',
      age: '18',
      phoneNumber: '1888888888'
    }

    /**
     * star: 目标对象 
     * handle: 具体的操作 
     **/
    var proxy = new Proxy(star, {
      get: function (target, getPropName, proxy) {
        // console.log(target, getPropName, proxy, '访问了值')

        if (getPropName == 'phoneNumber') {
          return '我不告诉你'
        }
        return target[getPropName]
      },
      set: function () {

      }
    })
    // console.log(proxy.phoneNumber)


    // 需求: 
    // var arr = [1,2,3,4]
    // arr[-1] 获取到下标最后一项

    function createArray(arr) {
      let handle = {
        get(target, index, proxy) {
          index = Number(index)
          if (index < 0) {
            index += target.length;
          }

          // Reflect 反射类似于 return target[index]
          return Reflect.get(target, index, proxy)
        }
      }

      return new Proxy(arr, handle);
    }

    arr = createArray([1, 2, 3, 4])

    // console.log(arr[-2])

  </script>


  <script>
    // vue2 Object.defineProperty 实现数据监听

    let obj1 = {
      name: 'mike',
      like: ['football', 'baskball'],
      score: { js: 88, php: 92 }
    }
    //重新定义数组处理方法,添加监听 (重写array数组方法)
    function reWriteArrayFn(obj) {
      const arrayFn = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'];
      arrayFn.forEach(method => {
        obj[method] = function (val) {
          // 继承Array的方法
          Array.prototype[method].apply(obj, arguments);
          console.log(`操作数组 ${method} ${val}`);
        }

      })
    }

    // 通过defineProperty监听数据的获取和修改
    function defineProp(obj, key, value) {
      Object.defineProperty(obj, key, {
        // value:默认值为undefined, 为对象的属性赋值
        configurable: true, // 默认值为 false, 控制属性是否可以从对象中删除以及其特性(除了 value 和 writable)是否可以更改。
        enumerable: true, // 默认值为 false,控制属性是否可以被遍历
        // writable: false, // 默认值为 false,控制属性是被可以被修改
        get() {
          console.log('获取数据', value)
          return value;
        },
        set(newVal) {
          console.log('修改数据', newVal)
          value = newVal
        },
      })
    }

    // 这里循环对象,通过defineProperty监听对象上的每一个属性,array对象单独处理
    function mapFn(obj) {
      if (typeof obj !== 'object' || obj == null) return;
      Object.keys(obj).forEach(key => {
        if (typeof obj[key] === 'object' && obj[key] !== null) {
          if (Array.isArray(obj[key])) {
            reWriteArrayFn(obj[key]);
          } else {
            mapFn(obj[key]);
          }
        };
        defineProp(obj, key, obj[key]);
      });
    }

    mapFn(obj1)

    // 修改数组
    obj1.like.push(111)

    console.log(obj1.name)

  </script>
</body>

</html>

 

posted @ 2024-09-12 16:09  男孩亮亮  阅读(12)  评论(0编辑  收藏  举报