深层次数据变化如何逐层往上传播?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的任务循环机制?
写的很乱,脖子和腰都在抗议了,抽空从第一个任务写起来,今天先合电脑睡觉??