美文网首页前端开发网页前端后台技巧(CSS+HTML)
Vue响应式原理之Object.defineProperty()

Vue响应式原理之Object.defineProperty()

作者: 前端王睿 | 来源:发表于2020-11-01 21:53 被阅读0次

    已经用Vue好几年了,也知道Vue 2.x的底层响应式用的是Object.defineProperty()这个方法来实现的,可一直没有去深入了解过这个方法,今天就让我们一起去探个究竟。

    一、对象属性的增删改查

    在开始之前,我们先简单了解下如何对对象进行增、删、改、查等操作。

    为了更加直观,我们直接上个简单例子。

    首先创建一个空对象data

    let data = {};
    

    然后我们可以对其进行增删改查操作。

    1. 新增属性

    data.text = '想学习更多前端知识,请关注公众号:前端微站';
    

    2. 删除属性

    delete data.text;
    

    3. 修改属性

    data.text = '想学习更多前端知识,请关注公众号:前端微站';  // 新增属性
    data.text = '关注公众号前端微站,学习更多前端知识';  // 修改属性
    

    4. 查询属性

    查询有两种,一种是通过属性名(key)查询属性值(value), 另一种则正好相反,是通过属性值(value)来查询属性名(key),不过这两种查询都可以通过for...in来遍历查询。

    // 先给对象再新增一个属性
    data.name = '前端王睿';
    
    // 通过 key 查询 value
    for(let key in data){
      if(key === 'name'){
        console.log(data[key]);  // 前端王睿
      }
    }
    
    // 通过 value 查询 key
    for(let key in data){
      if(data[key] === '前端王睿'){
        console.log(key);  // name
      }
    }
    

    二、对象的属性描述符

    上面所讲到的对象操作应该是司空见惯、众所周知的了,可是,你说好不容易找了个对象,说让你改就改,让你删就删,多没面子!有没有什么办法,我创建好了对象之后,就不让再随意改动呢,至少要我自己能够配置呀!

    当然有!这时Object.defineProperty()就派上用场了!我们可以通过Object.defineProperty()对对象属性进行描述,也就是告诉我们哪个属性是不能改的,哪个属性是不能删的,等等。这里就要讲到对象的属性描述符,而属性描述符又分为 数据描述符存取描述符。数据描述符是一个具有值的属性,该值可以是可写的,也可以是不可写的。存取描述符是由 getter 函数和 setter 函数所描述的属性。

    1. 数据描述符

    我们同样按照增、删、改、查的顺序对其进行一一列举,可以看出通过Object.defineProperty()方式新增的对象属性,默认都是不可被删除、修改和枚举的。

    value
    表示该对象属性的值,默认值为undefined。例如:

    let data = {};
    Object.defineProperty(data,'text',{
      value: '想学习更多前端知识,请关注公众号:前端微站'
    });
    console.log(data);  //  {text: '想学习更多前端知识,请关注公众号:前端微站'}
    

    这看起来跟直接通过data.text = '想学习更多前端知识,请关注公众号:前端微站'新增对象属性的效果一样,具体区别请接着往下看。

    configurable
    默认值为false,表示该对象属性不能被删除,只有为true时才可删除。例如:

    let data = {};
    Object.defineProperty(data,'text',{
      value: '想学习更多前端知识,请关注公众号:前端微站'
    });
    delete data.text;  // 删除失败
    console.log(data.text);   // 想学习更多前端知识,请关注公众号:前端微站
    

    删除没效果嘛!这时只需设置configurable值为true即可继续愉快地删除了。

    let data = {};
    Object.defineProperty(data,'text',{
      value: '想学习更多前端知识,请关注公众号:前端微站',
      configurable: true
    });
    delete data.text;  // 删除成功
    console.log(data.text);   // undefined
    

    writable
    默认值为false,表示该对象属性不能被修改,只有为true时才可修改。例如:

    let data = {};
    Object.defineProperty(data,'text',{
      value: '想学习更多前端知识,请关注公众号:前端微站'
    });
    data.text = '关注公众号前端微站,学习更多前端知识';  // 修改失败
    console.log(data.text);   // 想学习更多前端知识,请关注公众号:前端微站
    

    修改失败了!这时给它设置writable值为true即可继续愉快地修改了。

    let data = {};
    Object.defineProperty(data,'text',{
      value: '想学习更多前端知识,请关注公众号:前端微站',
      writable: true
    });
    data.text = '关注公众号前端微站,学习更多前端知识';  // 修改成功
    console.log(data.text);   // 关注公众号前端微站,学习更多前端知识
    

    enumerable
    默认值为false,表示该对象属性不能被枚举,只有为true时才可枚举。例如:

    let data = {
      text: '想学习更多前端知识,请关注公众号:前端微站'
    };
    Object.defineProperty(data,'name',{
      value: '前端王睿'
    });
    console.log(data);  //  {text: "想学习更多前端知识,请关注公众号:前端微站", name: "前端王睿"}
    for(let key in data){
      console.log(data[key]);  // 想学习更多前端知识,请关注公众号:前端微站
    }
    

    我们发现,data对象中虽然已经有两个属性,可我们发现最终却只能遍历出text这一个属性。这时只需给name属性设置enumerable值为true即可愉快地枚举出来了。

    let data = {
      text: '想学习更多前端知识,请关注公众号:前端微站'
    };
    Object.defineProperty(data,'name',{
      value: '前端王睿',
      enumerable: true  //  想学习更多前端知识,请关注公众号:前端微站
    });
    console.log(data);  //  {text: "想学习更多前端知识,请关注公众号:前端微站", name: "前端王睿"}
    for(let key in data){
      console.log(data[key]);
      // 想学习更多前端知识,请关注公众号:前端微站
      // 前端王睿
    }
    

    2. 存取描述符

    以下这两个函数就是Vue中用于进行数据监听的Object.defineProperty()中属性描述符的两个核心方法。
    get
    属性的 getter 函数,默认值为undefined。当访问该属性时,会调用此函数,返回值会被用作该属性的值。例如:

    let data = {
      text: '想学习更多前端知识,请关注公众号:前端微站'
    };
    Object.defineProperty(data,'text',{
      get(){
        return '关注公众号前端微站,学习更多前端知识'
      }
    });
    console.log(data);  // {text: "关注公众号前端微站,学习更多前端知识"}
    

    可以看到,此时data其实已经被我们修改了!

    set
    属性的 setter 函数,默认值为undefined。当属性值被修改时,会调用此函数,该方法接受一个参数,也就是被赋予的新值。例如:

    let data = {
      text: '想学习更多前端知识,请关注公众号:前端微站'
    };
    Object.defineProperty(data,'text',{
      set(value){
        console.log(value);  // 关注公众号前端微站,学习更多前端知识
      }
    });
    data.text = '关注公众号前端微站,学习更多前端知识';
    

    *注意:valuewritable不能与存取描述符(getset)同时存在,不然会报错!这是因为两者之间可能会存在互斥关系,例如:value值与get返回值不同,writablefalse而使用get却可改变对象属性的值,等等。

    三、使用Object.defineProperty()写个简单的响应式渲染

    <input id="input" type="text">
    <p id="text"></p>
    
    let oText = document.getElementById('text'),
        oInput = document.getElementById('input');
    let data = {
      text: ''
    };
    Object.defineProperty(data,'text',{
      set(value){
        oText.innerHTML = value; // 当修改data.text值时,自动更改oText中的文字内容
      }
    });
    oInput.value = data.text = '前端微站';
    oInput.addEventListener('keyup', function () {
      data.text = this.value;
    });
    

    重点总结:

    Object.defineProperty()中的 数据描述符 可以用来禁止对象属性的 删除、修改和枚举 操作
    Object.defineProperty()中的 存取描述符 可以用来对 对象赋值获取属性值 进行拦截操作

    结束语:

    看我在这啰嗦了半天,可最终能不能掌握Object.defineProperty()的用法还是应该在实践中多总结,而不是简单地看完了就结束了。最后给大家安排个作业,利用Object.defineProperty()的存取描述符,实现一个超简易的Vue,具体功能就是实现上面这个简单的响应式渲染功能。

    相关文章

      网友评论

        本文标题:Vue响应式原理之Object.defineProperty()

        本文链接:https://www.haomeiwen.com/subject/fmeyvktx.html