使用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.最後

相關文章
相關標籤/搜索