【ife】動態數據綁定(三)本身的實現

題目

深層次數據變化如何逐層往上傳播?html

let app2 = new Observer({
    name: {
        firstName: 'shaofeng',
        lastName: 'liang'
    },
    age: 25
});

app2.$watch('name', function (newName) {
    console.log('個人姓名發生了變化,多是姓氏變了,也多是名字變了。')
});

app2.data.name.firstName = 'hahaha';
// 輸出:個人姓名發生了變化,多是姓氏變了,也多是名字變了。
app2.data.name.lastName = 'blablabla';
// 輸出:個人姓名發生了變化,多是姓氏變了,也多是名字變了。

觀察到了嗎?firstName 和 lastName 做爲 name 的屬性,其中任意一個發生變化,都會得出如下結論:"name 發生了變化。"這種機制符合」事件傳播「機制,方向是從底層往上逐層傳播到頂層。git

這現象想必大家也見過,好比:「點擊某一個DOM元素,至關於也其父元素和其全部祖先元素。」(固然,你能夠手動禁止事件傳播) 因此,這裏的本質是:"瀏覽器內部實現了一個事件傳播的機制",你有信心本身實現一個嗎?github


個人代碼

function Observer(obj) {
  this.data = obj; // 對象掛載點
  this.$p = Array.prototype.slice.call(arguments,1)[0] || 'data'; // 實現事件冒泡儲存父級 名字
  this.transformAll(obj); // 對obj對象進行 遍歷, 而後調用convat 進行defineProperty改寫
}

Observer.prototype.transformAll = function(obj) {
  var keyarr = Object.keys(obj);
  for (var i=0,len=keyarr.length;i<len;i++) {
    var key = keyarr[i];
    var value = obj[keyarr[i]];
    if (value instanceof Object ) {
        new Observer(value,this.$p + '.' + key); // value此時爲對象,new Observer()會修改此對象,修改的結果展現在了obj當中。
    } else {
      this.convat(key, value, this.$p); // $p  傳入父key
    }
  }
}

Observer.prototype.content = {}

Observer.prototype.convat = function(key, val, $p) {
  var self = this;
  Object.defineProperty(self.data, key, {
    get: function(){
      console.log('你訪問了 ' + key);
      return val;
    },
    set: function(newval){
      var allkey = $p+ '.' + key;
      console.log('你設置了 '+ key + ', ' + '新的值爲 ' + newval);
      self.emit(allkey, newval);  // 觸發形式爲 father.child    newval爲傳入信息
      if (newval instanceof Object ) {
        new Observer(newval, allkey);  // 若是改寫爲對象
      }
      val =  newval
    },
    enumerable : true,
    configurable : true
  });
}
Observer.prototype.$watch = function(name, fn) {
  if (!this.content[name]) {
    this.content[name] = [];
  }
  this.content[name].push(fn);
  return this;
}
Observer.prototype.emit = function(name) {
  if (name.indexOf('.') !== -1) {
    var parent = name.split('.');
    for (var i =0;i<parent.length;i++){
      this.emit(parent[i]);
    }
  }

  var info = Array.prototype.slice.call(arguments,1);

  if (this.content[name]) {   // 遍歷同名事件倉庫 執行
    for (var i =0,len=this.content[name].length;i<len;i++) {
      var fn = this.content[name][i];
      fn.apply(this, info);
    }
  }

  return this;
}

Observer.prototype.off = function(name, fn) {  // 解除綁定,若是不傳入函數名則取消全部同名事件
  if (!fn) {
    this.content[name] = null;
    return this;
  }
  var index = this.content[name].indexOf(fn);
  this.content[name].splice(index, 1);
  return this;
}

題目連接個人代碼瀏覽地址(打開控制檯查看)瀏覽器


實現方式

在動態數據綁定(二)中須要實現一個事件系統,個人實現是在原型鏈上創建一個content屬性保存全部須要綁定的事件名稱觸發函數.以下:app

Observer.prototype.content = {}函數

事件系統應該是能監聽全部的實例化對象綁定的函數,在判斷改寫和深度convat當中都會建立一個新的實例化對象,若是寫到this上就沒法通用了。this

還有一個痛點是如何知道父級對象的key值,好在函數裏面對基本類型和對象類型的區分十分明瞭,只要在檢測到是對象類型的哪一條路上多傳入一個參數,傳入當前的屬性的key給下層,下層再利用這個key就行了。prototype

想要獲得冒泡,觸發事件的時候就必定要攜帶上父級的key信息,我使用了code

new Observer(value,this.$p + '.' + key);
set: function(newval){
      var allkey = $p+ '.' + key;
      console.log('你設置了 '+ key + ', ' + '新的值爲 ' + newval);
      self.emit(allkey, newval);  // 觸發形式爲 father.child    newval爲傳入信息
      if (newval instanceof Object ) {
        new Observer(newval, allkey);  // 若是改寫爲對象
      }
      val =  newval
    }

emit($p+ '.' + key, newval),觸發的時候就變成了data.name.firstName的形式,傳入的$p必定是保存着全部上層的key值,接着再在emit函數內部解析一下 ,觸發datanamefirstName便可。orm

有個問題,如今函數的執行順序是由上到下了,明天寫個setTimeout,恰好能夠加深理解js的任務循環機制?

寫的很亂,脖子和腰都在抗議了,抽空從第一個任務寫起來,今天先合電腦睡覺??

相關文章
相關標籤/搜索