|  | # 简略版+自己的注释 | // 判断一个变量是否是对象 function isObject(obj) {
 return obj.constructor === Object
 }
 class Observer {
 constructor(value) {
 this.value = value;
 if (!arr.isArray(value)) {
 this.walk(value);
 }
 }
 walk(obj) {
 const keys = Object.keys(obj);
 // 循环将obj中的每一个属性转换成getter/setter进行变化追踪
 for (let i = 0; i < keys.length; i++) {
 defineReactive(obj, keys[i], obj[keys[i]]);
 }
 }
 }
 function defineReactive(data, key, val) {
 if (isObject(val)) {
 new Observer(val); // 进行递归调用
 }
 let dep = new Dep();
 Object.defineProperty(data, key, {
 configurable: true,
 enumerable: true,
 get: function () {
 dep.depend();
 return val;
 },
 set: function (newVal) {
 if (val === newVal) return
 // 如果赋值的新值也是一个对象 也需要进行侦测
 if (isObject(newVal)) {
 new Observer(val); // 进行递归调用
 }
 val = newVal;
 dep.notify(); // 通知所有的订阅者,数据要被修改了,做出相应的行为(也就是执行对应的回调函数)
 }
 })
 }
 class Dep {
 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() {
 const subs = this.subs.slice();
 for (let i = 0; i < subs.length; i++) {
 subs[i].update(); // 在这个地方执行回调函数
 }
 }
 }
 function remove(arr, item) {
 if (arr.length) {
 const index = arr[item];
 if (index > -1) {
 return arr.splice(index, 1);
 }
 }
 }
 class Watcher {
 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变量上
 let value = this.getter.call(this.vm, this.vm); // 该代码的作用很关键
 // 如果expOrFn直接是一个表达式不是一个函数 eg: 'name', 'age' 假设只有一个属性 不是这种的 'name.a.b'
 // 我们就可以直接下面这样写,只是为了测试理解,vue.js源码处理更加全面
 // let value = this.vm // vm就是要监听的数据,当然expOrFn要在constructor中挂在到this身上
 window.target = undefined;
 return value;
 }
 update() {
 const oldValue = this.value;
 this.value = this.get();
 this.cb.call(this.vm, this.value, oldValue);
 }
 }
 // 该方法的作用是将 'name.a.b'这种表达式的值取出来
 // 也就是通过该方法返回的是 obj['name']['a']['b]的值
 function parsePath(path) {
 const bailRE = /[^\w.$]/;
 const segments = path.split('.')
 // 闭包
 return function (obj) {
 for (let i = 0; i < segments.length; i++) {
 if (!obj) return
 obj = obj[segments[i]] // 取出属性的值 所以这个地方会执行getter
 }
 return obj; // 返回属性的值
 }
 }
 <script>
 let btns = document.getElementsByTagName('button');
 // 定义数据
 let person = {};
 // 定义响应式数据的属性
 defineReactive(person, 'name', '乔峰')
 // 监听数据
 let w = new Watcher(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方法来监听这个响应式数据的变化,然后就能做到数据驱动,或者叫响应式数据
 
 ______如有不对,请指正_______
 
 
 
 ----------------------------
 原文链接:https://blog.51cto.com/11871779/2507844
 
 程序猿的技术大观园:www.javathinker.net
 
 
 
 [这个贴子最后由 flybird 在 2021-02-18 10:13:09 重新编辑]
 |  |