本文是要实现对象使用Object.defineProperty重写对象,例

//例把下面对象obj
let obj={a:1};
//修改成以下写法
let aValue=1;
Object.defineProperty(obj,a,{
  get(){
    return aValue;
  },
  set(newValue){
    if(aValue===newValue) return;
    aValue=newValue;
  }
})
这样在对对象属性进行获取、修改时,都能在get、set中监测到对象属性变化,但不能对对象属性值为数组进行监测,需要重写数组方法,下一篇

首先介绍Object.defineProperty:

  作用是给对象的属性设置get、set、value、writable(可重写)、configurable(可配置、删除)、enumerable(可遍历)等

  其中writable不能和get、set一起出现,get、set不能和value一块出现

Object.defineProperty(obj,key,{
  value,//属性值
  writable,//boolean
  configurable,//boolean
  enumerable,//boolean
  get(){},//不能和writable、value一块配置
  set(newValue){}//不能和writable、value一块配置
})

 

******************************正文开始********************************

在使用vue时

//在这里只对vue的data进行处理
let vm = new Vue({
  data:function(){
    return {
      a:1,
      b:{c:2}
    }
  }
})
可以把以上改成
let options=
{
data:function(){
  return {
      a:1,
      b:{c:2}
    }
  }
}
let vm = new Vue(options)

 

模仿以上,创建一个函数命名为Vue,

function Vue(options){
}

问题:Vue如何将options里的data转成Object.defineProperty的写法

  解剖问题:

    1、在new  Vue()时即完成了到Object.defineProperty的转换

    解决:在new Vue()时执行了一个方法,命名为_init

function Vue(options){
  this._init(options);
}
Vue.prototype._init=function(options){
  console.log("new的时候执行了_init")
}
解析 new Vue()会将所有的属性实例化,所以this_init()也会被执行,执行的时候会在对象的原型链中查找同名方法,所以会找到Vue.prototype._init方法,我们可以在_init中对options进行处理

开始正题

1、封装Object.defineProperty

function defineReactive(obj,key,value){
  Object.defineProperty(obj,key,{
    get(){
      return value;
    },
    set(newValue){
      if(newValue===value) return;
      value=newValue;
    },
    configurable:true,
    enumerable:true
  })
}

2、封装判读当前变量是不是需要被观察

function observe(data){
  if(typeof data!=="object"||data===null){return}
  walk(data);
}

3、遍历对象并设置Object.defineProperty

function walk(data){
  let keys=Object.keys(data);
  for(let i=0;i<keys.length;i++){
    defineReactive(data,keys[i],data[keys[i]])
  }
}

以上会有一个问题,如果是{a:1}这种则没问题,如果{a:1,b:{c:2}},则不能遍历到底层,所以需要在设置get前对设置的值进行判断当前值需不需要被观察,如果需要被观察,先观察,不需要直接给当前属性赋值

function defineReactive(obj,key,value){
  observe(value);//判断当前值需不需要被观察,需要观察,先观察再进行下面的处理
  Object.defineProperty(obj,key,{
    get(){
      return value;
    },
    set(newValue){
      if(newValue===value) return;
      value=newValue;
    },
    configurable:true,
    enumerable:true
  })
}

到此还有一个问题,就是给对象的某一个属性设置对象时,应当对设置的值进行观察,看是不是需要把值设计成 Object.defineProperty

function defineReactive(obj,key,value){
  observe(value);//判断当前值需不需要被观察,需要观察,先观察再进行下面的处理
  Object.defineProperty(obj,key,{
    get(){
      return value;
    },
    set(newValue){
      if(newValue===value) return;
      value=newValue;
      observe(newValue);
    },
    configurable:true,
    enumerable:true
  })
}

至此初步设计完成,当然还有一个问题,就是对数组处理会有问题

obj ={
  a:[2,3]
}
obj.a.push(4);
//并不能监测数组属性的改变,需要重写数组方法,下一章介绍

还需要解决一个问题,现在访问上面的数据是根据vm._data.key这样去访问,但是在使用vue时,是通过vm.key去访问数据的

//因为我们给data里每一层属性都添加了Object.defineProperty 
data(){
  return{
    a:1,
    b:{
      c:2
    }
  }
}
//a\b\c三个均设置了Object.defineProperty,在访问属性c的时候,会首先触发b接着才会访问c,所以只需要在最外层设置代理即可
//实现访问vm.a等同于访问vm._data.a
proxy(vm,source,key){
  Object.defineProperty(vm,key,{
    get(){
      return vm[source][key]
    },
    set(newValue){
      vm[source][key]=newValue
    }
  })
}


修改initData函数
function initData(vm){
  let data=vm.$options.data;
  data=vm._data=typeof data==="function"?data.call(vm):data||{};
  for(let key in data){
    proxy(vm,"_data",key);
  }
  observe(data);
}

 

posted on 2021-10-15 14:15  沉默的末1993  阅读(75)  评论(0编辑  收藏  举报