深層次數據變化如何逐層往上傳播?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
函數內部解析一下 ,觸發data
、name
、firstName
便可。orm
有個問題,如今函數的執行順序是由上到下了,明天寫個setTimeout
,恰好能夠加深理解js的任務循環機制?
寫的很亂,脖子和腰都在抗議了,抽空從第一個任務寫起來,今天先合電腦睡覺??