# 简略版+自己的注释//判断一个变量是否是对象
functionisObject(obj){
returnobj.constructor===Object
}
classObserver{
constructor(value){
this.value=value;
if(!arr.isArray(value)){
this.walk(value);
}
}
walk(obj){
constkeys=Object.keys(obj);
//循环将obj中的每一个属性转换成getter/setter进行变化追踪
for(leti=0;i
defineReactive(obj,keys[i],obj[keys[i]]);
}
}
}
functiondefineReactive(data,key,val){
if(isObject(val)){
newObserver(val);//进行递归调用
}
letdep=newDep();
Object.defineProperty(data,key,{
configurable:true,
enumerable:true,
get:function(){
dep.depend();
returnval;
},
set:function(newVal){
if(val===newVal)return
//如果赋值的新值也是一个对象也需要进行侦测
if(isObject(newVal)){
newObserver(val);//进行递归调用
}
val=newVal;
dep.notify();//通知所有的订阅者,数据要被修改了,做出相应的行为(也就是执行对应的回调函数)
}
})
}
classDep{
constructor(){
this.subs=[]//这个里面存放的是Watch实例对象
}
addSub(sub){
this.subs.push(sub);//在这个地方收集订阅者
}
removeSub(sub){
remove(this.subs,sub);
}
depend(){
if(window.target){
this.addSub(window.target);//在这个地方触发depend方法,进行收集订阅者
}
}
notify(){
constsubs=this.subs.slice();
for(leti=0;i
subs[i].update();//在这个地方执行回调函数
}
}
}
functionremove(arr,item){
if(arr.length){
constindex=arr[item];
if(index>-1){
returnarr.splice(index,1);
}
}
}
classWatcher{
constructor(vm,expOrFn,cb){
this.vm=vm;
this.getter=parsePath(expOrFn);
this.cb=cb;
this.value=this.get();//获取expOrFn中的值在这个内部同时会触发getter,从而在dep中添加自己
}
get(){
window.target=this;//将watch实例对象赋值给全局的target变量上
letvalue=this.getter.call(this.vm,this.vm);//该代码的作用很关键
//如果expOrFn直接是一个表达式不是一个函数eg:'name','age'假设只有一个属性不是这种的'name.a.b'
//我们就可以直接下面这样写,只是为了测试理解,vue.js源码处理更加全面
//letvalue=this.vm[this.expOrFn]//vm就是要监听的数据,当然expOrFn要在constructor中挂在到this身上
window.target=undefined;
returnvalue;
}
update(){
constoldValue=this.value;
this.value=this.get();
this.cb.call(this.vm,this.value,oldValue);
}
}
//该方法的作用是将'name.a.b'这种表达式的值取出来
//也就是通过该方法返回的是obj['name']['a']['b]的值
functionparsePath(path){
constbailRE=/[^\w.$]/;
constsegments=path.split('.')
//闭包
returnfunction(obj){
for(leti=0;i
if(!obj)return
obj=obj[segments[i]]//取出属性的值所以这个地方会执行getter
}
returnobj;//返回属性的值
}
}
letbtns=document.getElementsByTagName('button');
//定义数据
letperson={};
//定义响应式数据的属性
defineReactive(person,'name','乔峰')
//监听数据
letw=newWatcher(person,'name',function(newVal,oldVal){
console.log('数据发生变化了')
console.log(newVal,oldVal);
})
//1.取值操作
person.name
//2.改变数据
btns[0].onclick=function(){
person.name='小龙女';
}
# 总结
1.在实例化一个Watcher对象时,其get方法中会触发defineReactive中的getter访问器,在其getter访问器中会执行dep.depend()方法,dep.depend()方法会调用this.subs.push(sub)方法,从而收集依赖,也就是在subs 中存放的是当前的Watcher实例对象
2.Watch实例对象自身必须有一个update方法
3.当数据发生变化时,会触发defineReactive中的setter访问器,在其setter访问器中会调用subs[i].update()方法,其中的每一个subs[i]就是watch实例对象,从而执行了update方法,在update方法中执行了this.cb.call(this.vm, this.value, oldValue)回调函数
3.Observer 类是用来将传递进来的数据遍历转换成响应式数据,也就是转换成getter/setter的形式进行侦测
4.在使用的时候,我们首先需要调用defineReactive方法,来创建一个响应式数据,然后再调用Watcher方法来监听这个响应式数据的变化,然后就能做到数据驱动,或者叫响应式数据
______如有不对,请指正_______
如果觉得《vue 监听表格里的数据变化_vue中监听object数据变化的基本原理》对你有帮助,请点赞、收藏,并留下你的观点哦!