实现vue的一些核心API

68 阅读3分钟

对象规则限制

  • 我们在项目中书写对象成员和调用浏览器内置的对象API时候,经常会发现一种情况:自己设置的成员 可以枚举,但是浏览器内置的则 不可枚举

  • 我们在查看对象中某个属性或者某个对象中所有属性的规则时,有以下两种方法:

    • Object.getOwnPropertyDescriptor(objName,[attr])
    • Object.getOwnPropertyDescriptors(objName)
  • 任何一个对象的属性/方法,都存在自己的规则: (不)可枚举、(不)可修改、(不)可删除

    let obj = {
        x: 123;
    }
    Object.getOwnPropertyDescriptor(obj, 'x');
    ===> 
    {
        configurable: true,  是否可删除
        enumerable: true, 是否可枚举 
        writable: true,  是否可修改
        value: 123,  成员值
    }
    
  • 当我们使用 getOwnPrototypeSescriptor 进行检测时候,可以得到四个属性

    • configurable 是否可以删除
    • enumerable 是否可枚举
    • writable 是否可修改
    • value 成员值
    • 当我们自己创建的私有属性成员,我们可以看到三个属性都是 TRUE,都是 可枚举、可删除、可修改的
  • Object.getOwnPropertyDescriptor(Object.prototype, 'toString')
    ====>
    {
        configurable: true,
        enumerable: false,
        value: ƒ toString(),
        writable: true,
    }
    
    • 检测浏览器内置属性/方法: 不可枚举、可删除、可修改

自定义属性规则

  • 当我们也想修改自定义的属性和方法规则时:Object.defineProperty

    Object.defineProperty(Object, attr,{
        // 规则设置
    })
    
  • defineProperty 特点:

    • 成员存在,则修改该成员规则;
    • 成员不存在,则新增该成员并创建规则
    let obj = { x: 123 }
    Object.defineProperty(obj, 'x', {
        configurable: false,
        enumerable: false,
        value: 999,
        writable: true
    });
    ​
    Object.defineProperty(obj, 'name', {
        configurable: false,
        enumerable: true,
        value: 'wj',
        writable: true
    })
    
    • 使用defineProperty , 新增成员,默认规则均为 FALSE
    • 使用defineProperty, 修改成员,之前是什么规则,现在依然是什么规则
    let obj = { x: 123 }
    Object.defineProperty(obj, 'x', {
        value: 999,
    });
    
    Object.defineProperty(obj, 'name', {
        value: 'wj',
    });
    
    log(Object.getOwnPropertyDescriptors(obj));
    
    ==>
    
    name: 
        {value: 'wj', writable: false, enumerable: false, configurable: false}
    x: 
        {value: 999, writable: true, enumerable: true, configurable: true}
    
  • defineProperty 可以对 对象中的属性 进行“数据劫持”

    • 获取成员值时,触发 get 函数,get的返回值就是成员访问的结果;

    • 设置成员值时,触发 set 函数,形参对应的val值就是最新设置的值;

    • 设置 get\set 后,不能设置value & writable 属性,两者属于冲突状态。

      let obj = { x: 123 };
      let proxy = { ...obj };
      ​
      Object.defineProperty(obj,'x',{
          get() {
              // return obj.x; // 死循环  不断触发 get函数
              return proxy.x;
          }
          set(val) {
              // obj.x = val; // 死循环,不断触发 set 函数
              proxy.x = val;
          }
      })
      
    • 为啥要进行代理:如果获取obj.x,进行get操作,返回还是本身,那么就会不断触发get。进而会死循环。所以需要有个代理对象,这个对象和原对象的数据完全相同,每次代理到这个对象上进行操作。

ES6 Proxy

  • proxy: 创建一个对象的代理,实现对其基本操作的拦截和自定义。【比如查找、赋值、枚举、调用等】

    let obj = {
        a: 111,
        b: 222,
        arr: [333, 444],
        o: {x: 1}
    }
    ​
    let p1 = new Proxy(obj, {
        get(target, key, value) {
            log('获取',target,key);
            return Reflect.get(target, key)
        },
        set(target, key, value) {
            log('获取', target, key, value);
            return Reflect.set(target, key, value)
        },
        deleteProperty(target, key) {
            log('删除', target, key);
            return Reflect.deleteProperty(target, key)
        }
    })
    
    • p1 : 代理对象
    • Reflect 可以返回操作后的结果。
    • 我们后期通过p1 来操作数据,然后通过Reflect 来修改原数据,返回是否成功的结果。
  • 优势:

    • 直接对整体对象进行数据劫持。在vue2中使用defineProperty 需要循环每一项,判断是否为数组进行分别劫持处理。proxy可以直接对对象进行数据劫持
    • 不仅可以进行set、get的操作,还可以对删除、查找has等进行操作。