使用defineProperty进行Object变化侦测

1. 使用defineProperty定义基础数据

function defineReactive(data, key, val) {
  Object.defineProperty(data, key, {
    enumerable: true,
    configurable: true,
    get: function() {
      return val;
    },
    set: function(newVal) {
      if (val === newval) {
        return;
      }
      val = newVal;
    }
  });
}
复制代码

2. 为数据添加依赖收集和依赖通知

在getter中触发依赖收集,在setter中触发依赖通知。javascript

function defineReactive(data, key, val) {
  const dep = [];
  Object.defineProperty(data, key, {
    enumerable: true,
    configurable: true,
    get: function() {
      //依赖收集
      dep.push(window.target);
      return val;
    },
    set: function(newVal) {
      if (val === newVal) {
        return;
      }
      //依赖通知
      for (let index = 0; index < dep.length; index++) {
        dep[index].update(newVal, val);
      }
      val = newVal;
    }
  });
}
复制代码

2.1 抽象出依赖管理对象Dep

Dep对象将负责收集、保存依赖,在属性变化时经过执行依赖的update()方法通知依赖。java

class Dep {
  constructor() {
    this.Deps = [];
  }
  addDep() {
    if (window.target) {
      this.Deps.push(window.target);
    }
  }
  notify(newVal, val) {
    for (let index = 0; index < this.Deps.length; index++) {
      this.Deps[index].update(newVal, val);
    }
  }
}
复制代码

这里的window.target不只是依赖对象与Dep对象沟通的纽带,同时也能够经过判断window.target值在后续读取该属性避免屡次重复绑定依赖。函数

2.2 改写defineReactive

function defineReactive(data, key, val) {
  let deps = new Dep()
  Object.defineProperty(data, key, {
    enumerable: true,
    configurable: true,
    get: function() {
      //依赖收集
      deps.addDep();
      return val;
    },
    set: function(newVal) {
      if (val === newVal) {
        return;
      }
      //依赖通知
      deps.notify();
      val = newVal;
    }
  });
}
复制代码

最终defineReactive函数将实现对传入对象属性的转换,使其成为响应式数据。然而defineReactive函数是以对象属性为单位进行转换,咱们更但愿传入对象直接将其全部属性转换。ui

2.3 定义Observer

class Observer {
   constructor(value) {
       this.value = value;
       if (!Array.isArray(value)) {
           this.travel(value);
       }
   }
   travel(value) {
       const keys = Object.keys(value);
       for (let index = 0; index < keys.length; index++) {
           defineReactive(value, keys[index], value[keys[index]]);
       }
   }
}
复制代码

3. 定义依赖

  1. 什么是依赖?this

    依赖是一个对象,它能够获取、保存绑定的属性值,该属性值发生变化时它的update方法将被触发,从而执行保存值的更新以及绑定的回调函数的执行。spa

  2. 依赖的用处?3d

    经过依赖对象使外部代码能够与某属性产生关联,监听其变化并绑定回调函数。code

class Watcher {
 constructor(obj, proOrObj, fn) {
   this.obj = obj;
   this.getter = this.parsePath(proOrObj);
   this.fn = fn;
   this.val = this.get();
 }
 get() {
   if (!this.val) {
     window.target = this;
   }
   let val = this.getter.call(this.obj, this.obj);
   window.target = undefined;
   return val;
 }
 update() {
   let oldVal = this.val;
   this.val = this.get();
   this.fn(this.val, oldVal);
 }
 parsePath(path) {
   const pros = path.split(".");
   return function(obj) {
     for (let index = 0; index < pros.length; index++) {
       if (!obj) {
         return;
       }
       obj = obj[pros[index]];
     }
     return obj;
   };
 }
}
复制代码
  1. if (!this.val) {window.target=this;}cdn

    咱们须要将Watcher实例依赖添加到对象属性中,而在Dep对象中添加依赖都是经过window.target,因此咱们须要将Watcher实例赋值给window.target,而添加依赖只需在初次读取完成,后续若屡次读取该属性将重复添加相同依赖。server

  2. let val=this.getter.call(this.obj,this.obj);

    添加依赖是在属性的getter中执行因此咱们须要读取一次该属性,同时保存该属性值。这里的getter是parsePath()方法返回的一个函数,该函数接收一个对象经过递归解析该对象的嵌套属性路径(如:proOrObj = "a.b.c")并返回该属性值。

  3. 在属性的setter中咱们经过执行依赖的update()方法通知依赖,这里咱们将更新属性值而且执行回调函数。

4.定义数据并添加依赖

const man = {};
defineReactive(man, "age", 1);
const watcher = new Watcher(man, "age", function(newVal, oldVal) {
 console.log("age变化:新数据" + newVal + "老数据" + oldVal);
});
man.age = 2;  //age变化:新数据2老数据1
man.age = 3;  //age变化:新数据3老数据2
man.age = 4;  //age变化:新数据4老数据3
man.age = 5;  //age变化:新数据5老数据4
复制代码

5.最后

相关文章
相关标签/搜索