美文网首页技术干货Web 前端开发 前端开发那些事
ife.baidu笔记 | 动态数据绑定(二)

ife.baidu笔记 | 动态数据绑定(二)

作者: HelloCherry | 来源:发表于2017-03-12 23:57 被阅读0次

你踩过的坑会帮助你日后走得更快

Awesome Vuejs.png
动态数据绑定(二)
  • <a href="http://ife.baidu.com/course/detail/id/20">题目</a>
  • <a href="https://github.com/CaiYiLiang/2017ife-Baidu/blob/master/vue-DynamicDataBinding%2302.js">任务源码</a>
  • 考察知识点:
    递归Recursion
    发布-订阅模式

任务二主要涉及两大块:访问引用类型的属性,利用发布-订阅模式实现事件监听。

先讲讲如何解决“比较深”的属性

看到任务二,发现了在<a href="http://www.jianshu.com/p/3fb7c2a6b047">任务一</a>写的代码有一点问题,就是无法对“比较深”的对象的进行有效访问:

无法对“比较深”的对象的进行有效访问

在<a href="https://github.com/CaiYiLiang/2017ife-Baidu/blob/master/vue-DynamicDataBinding%2301.js">任务一</a>的代码中,为了使实例通过person1.data.进行属性访问,新建了原型对象属性Observer.prototype.data = {},但是当传入参数对象是一个“比较深”的对象(属性值也是对象),就无法建立如例子中的person1.data.address.add1属性访问,因此<a href="https://github.com/CaiYiLiang/2017ife-Baidu/blob/master/vue-DynamicDataBinding%2301.js">任务一</a>的代码需要重构。踩坑,会沉没一点时间成本,但也是一个自我发现和进步的机会。你踩过的坑会帮助你日后走得更快。

在<a href="https://github.com/CaiYiLiang/2017ife-Baidu/blob/master/vue-DynamicDataBinding%2302.js">任务二</a>中,关键的是在function函数中定义this.data = obj;令每个实例对象的data与传入的对象obj指向相同的内存空间(不要忘了,dataobj都是引用类型,它们的值都是指向同一个地址的"指针")。

当遍历到较深的属性时,利用递归new Observer(obj[key])实现较深的属性的getter/setter定义。同时,在对象实例设置新的值是一个对象的时候,也是利用递归new Observer(newValue)对新增的"较深"属性定义getter/setter响应。
不多说,上代码,只是<a href="https://github.com/CaiYiLiang/2017ife-Baidu/blob/master/vue-DynamicDataBinding%2301.js">任务一</a>的修正和拓展。

function Observer(obj){
  this.data = obj;
  this.walk(obj);
}

Observer.prototype.walk = function(obj) {
  Object.keys(obj).forEach(key => {
    let val = obj[key]
    console.log(key)
    if(typeof obj[key] === "object"){
      new Observer(obj[key])
    }

    Object.defineProperty(this.data,key,{
     enumerable: true,
     configurable: true,
     get:function(){ 
       console.log("You are visiting the attribute: "+ key +" - " + val)
       return val },
     set:function(newValue) {
       if(typeof newValue === 'object'){
       new Observer(newValue)
       } 
       console.log("You are updating the attribute: "+ key +" - "+ newValue)
       val = newValue
      }
  })
 })
}
访问"较深"的属性值

#######~~(╯﹏╰)b一不小心又是自己挖的坑
在写Object.definePropertygetter/setter的时候,遇到了一个Uncaught RangeError: Maximum call stack size exceeded,其中代码是这样的

 Object.defineProperty(this.data,key,{
 get:function(){ 
       console.log("You are visiting the attribute: "+ key +" - " + obj[key])
       return obj[key] },
....
}

getter里访问obj[key]就相当于陷入死循环无限调用get()方法,直到超过最大的栈数。


解决了任务二中的问题一和问题二,

现在了解发布-订阅模式
发布-订阅模式

观察者模式又叫做发布-订阅模式,它定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生改变时就会通知所有观察着对象。
看完定义有点懵逼,我把发布-订阅模式定义为以下三点:

  • 发布-订阅模式由两个角色组成:(事件)发布者和订阅者。
  • 订阅者向(事件)发布者进行注册(订阅),在事件发布时被通知。
  • 发布者在事件发生时向(之前在他这里进行注册/订阅的)订阅者发送通知。

这个情景是不是似曾相识?其实存在于我们生活的方方面面。

例如订阅周刊

小明(订阅者)对前端周刊(主题/发布者)有兴趣,于是交钱订阅每期的前端周刊(订阅/注册事件),并计划把每期收到的前端周刊(事件发布)带回学校阅读(订阅者在事件发生后所进行的动作)。

小红(订阅者)也对前端周刊(主题/发布者)有兴趣,于是也交钱订阅每期的前端周刊(订阅/注册事件),但小红只是想收藏书籍,所以就把每期收到的前端周刊(事件发布)放到家里书柜(订阅者在事件发生后所进行的动作)。

例如Github上的watch按钮

小明(订阅者)在github上建立了一个开源项目,他想在有人start了他的项目/有人向他提出issue/有人pull request的时候(主题/发布者)及时收到邮件通知,于是他点击了watch按钮(订阅/注册事件)进行通邮件知设置,以便他及时处理issue/PR(订阅者在事件发生后所进行的动作)。

代码如下:

// 主题发布者
function Publisher() {  
    this.subscribers = {};
    // 用于存储某事件对应的订阅者列表....
}

// 添加一个发布功能(方法),在事件发生时通知订阅者名单里的每一个人 
Publisher.prototype.publish = function(eventType) {  
    if(eventType in this.subscribers){
       this.subscribers[eventType].forEach(function(subscriberCb){
         subscriberCb();
       })
    }
}

// 观察者/订阅者
function Observer() {

}

// 订阅者有订阅/注册能力
Observer.prototype.subscribe = (publisher,eventType,cb) =>{    
  if(!publisher.subscribers.hasOwnProperty(eventType)){
    publisher.subscribers[eventType] = []
  }
  publisher.subscribers[eventType].push(cb)
}

测试
// 实例化
// 例1.
var techbook_Publisher = new Publisher()
var xiaoming = new Observer()
var xiaohong = new Observer()

//读者进行订阅/注册
xiaoming.subscribe(techbook_Publisher,"web-book",function(){
                   console.log("小明要把期刊带回学校阅读")
})
xiaohong.subscribe(techbook_Publisher,"web-book",function(){
                   console.log("小红要把期刊在家里收藏")
})

//出版社出版期刊,事件发生
techbook_Publisher.publish("web-book")

例1

再看看例2

// 例2.
var github = new Publisher()
var xiaoming = new Observer()

//用户对该仓库的动态进行订阅/注册
xiaoming.subscribe(github,"watch-this-repo",function(){console.log("小明的github-repo在有人提issue/PR时收到邮件通知")})

github.publish("watch-this-repo")
例2

当然,订阅者还会拥有取消订阅功能,以及在事件发生后不同订阅者可以有不同反应(或者有参数传入)。
因此,可以总结出发布-订阅模式的流程:

  1. 订阅者订阅某个主题(或者关注某个发布者)
  2. 某个主题(某个发布者)将该订阅者记录进待通知名单
  3. 在某个情景下主题发布,通知在待通知名单上的各个读者,各个读者进行各自的后续操作。

回归到<a href="https://github.com/CaiYiLiang/2017ife-Baidu/blob/master/vue-DynamicDataBinding%2302.js">任务二</a>,
实现订阅者的订阅功能Observer.prototype.$watch以及某时刻事件触发(发布)功能Observer.prototype.$change,并在对象实例属性值变化的时候调用Observer.prototype.$change

Observer.prototype.oberseredList = {}  //subscriber list,shared property with oberser&publisher
let oberseredList = Observer.prototype.oberseredList

Observer.prototype.$watch = (oberseredKey,cb) =>{    //subscriber register
  if(!oberseredList.hasOwnProperty(oberseredKey)){
    oberseredList[oberseredKey] = []
  }
  oberseredList[oberseredKey].push(cb)
}

Observer.prototype.$change = function(oberseredKey,newValue){  //Event Trigger
  let params = Array.prototype.slice.call(arguments,1)
  if(oberseredList.hasOwnProperty(oberseredKey)) { 
     oberseredList[oberseredKey].forEach(cb => {
      cb.apply(null,params)
     })
  }
  
}

还有关键在set里调用Observer.prototype.$change

 set:function(newValue) {
       ......
       Observer.prototype.$change.call(this,key,newValue)  //Event Trigger 
      ....
      }

/*Test Case*/
var person1 = new Observer({name:"xiaoming", age:20, 
address:{add1:"China",add2:"UK"} });
person1.data.age
person1.$watch('age', function(age) {
         console.log(`我的年纪变了,现在已经是:${age}岁了`)
 });
person1.data.age = 55
Test Case
Reference

系列目录

<a href="http://www.jianshu.com/p/3fb7c2a6b047">ife.baidu笔记 | 动态数据绑定(一)</a>

原创文章

简书:<a href="http://www.jianshu.com/u/c0600377679d">HelloCherry</a>
Github: <a href="https://github.com/CaiYiLiang">CaiYiLiang</a>
Girhub / vue-demos:

  • <a href="https://github.com/CaiYiLiang/simply-calculator-vuejs" >利用vue.js实现简易计算器</a>
  • <a href="https://github.com/CaiYiLiang/vue-demos/tree/master/wikipediaViewer-vuejs">实现简单的单页面应用(vue2.0,vue-router,vue-cliand ajax(jsonp))</a>
  • <a href="https://github.com/CaiYiLiang/vue-demos/tree/master/shoppingcart-vuejs" >利用vue.js,vuex,vue-router和 Element UI实现购物车场景</a>
    很高兴写的<a href="https://github.com/CaiYiLiang/vue-demos">vue demos</a>被收录到 <a href="https://github.com/vuejs/awesome-vue">awesome-vue</a>中,简直就是一朵小红花😋

如果觉得有一点点帮助,一个❤❤就是鼓励(。⌒∇⌒)

相关文章

  • ife.baidu笔记 | 动态数据绑定(二)

    你踩过的坑会帮助你日后走得更快 动态数据绑定(二) 题目 任务源码 考察知识点:递归Recursion发布-订阅模...

  • ife.baidu笔记 | 动态数据绑定(一)

    动态数据绑定(一) 题目 作业源码 考察知识点: Object.defineProperty(ES5) , Obj...

  • 动态数据绑定(二)

    动态数据绑定(一) vue早期源码学习系列之一:如何监听一个对象的变化 方法一

  • Vue 简单语法

    动态绑定数据message是动态的 判断语句 循环语句 事件监听 UI与数据双向绑定 数据只绑定一次,后续数据改变...

  • 小程序基础

    内容: 一:数据绑定 1.1 动态内容绑定:页面的.js文件中 数据绑定 1.2动态属性绑定 1.3三元表达式 1...

  • 微信小程序的数据绑定

    数据绑定 WXML中的动态数据均来自对应Page的data。 简单绑定 数据绑定使用"Mustache"语法(双大...

  • 小程序框架 ——视图层

    数据绑定 WXML中的动态数据均来自对应Page的data 简单的绑定 数据绑定使用Mustache语法(双大括号...

  • 动态数据绑定

    动态数据绑定(一) 我的github iSAM2016 实现的步骤: 1.监听对象属性的读取与变化 Object...

  • Angular--使用数据绑定

    1、数据绑定 数据绑定 是被嵌入模板的表达式,经过求值后可在HTML文档中生成动态内容单向数据绑定 从数据模型流向...

  • IFE2017,动态数据绑定(二)学习笔记

    自定义事件 我们知道JS里绑定事件有addEventListener()这个方法。在JS中我们可以这样创建一个自定...

网友评论

    本文标题:ife.baidu笔记 | 动态数据绑定(二)

    本文链接:https://www.haomeiwen.com/subject/jlzzgttx.html