Vue双向绑定原理(二)访问器属性defineProperty()和发布/订阅模式
2017-03-05 18:54
531 查看
访问器属性的介绍
参考资料:《JavaScript高级程序设计》(第三版)第六章js的对象有两种属性: 数据属性和访问器属性。
1.数据属性
数据属性包含一个数据值的位置。这个位置可以读取和写入值。数据属性也就是我们最常见的对象属性。数据属性有4个描述他行为的特性:
Configurable: 能否用delete删除属性从而重新定义属性。默认为true
Enumerable: 能否通过for-in遍历,即是否可枚举。默认为true
Writable: 是否能修改属性的值。默认为true
Value: 包含这个属性的数据值,读写属性的时候其实就在这里读写。默认为undefined
要修改属性的上述4个默认特性,就必须使用ECMAScript的Object.defineProperty()方法,该方法包含3个参数:属性缩在的对象,属性名,描述符对象。描述符对象的属性必须在上述4个属性中。例如:
var person = {}; Object.defineProperty(person,"name",{ writable: false, value: "Nicholas" }); alert(person.name); // "Nicholas" person.name = "Tom"; alert(person.name); // "Nicholas"
上例创建了一个不可写的name属性并赋值。所以无法修改。
注意,一旦把Configurable属性设置为false,就无法再将其变回true了,此时再想修改特性,就都会报错了。
2.访问器属性
访问器属性不包含数据值,他们包含一对getter和setter函数(非必须)。在读写访问器属性的值的时候,会调用相应的getter和setter函数,而我们的vue就是在getter和setter函数中增加了我们需要的操作。
访问器属性有以下4个特性:
Configurable: 能否用delete删除属性从而重新定义属性。默认true
Enumerable: 能否通过for-in遍历,即是否可枚举。默认true
get: 读取属性时调用的函数,默认undefined
set: 写入属性时调用的函数,默认undefined
var book = { _year: 2004, edition: 1 }; Object.defineProperty(book,"year",{ get: function(){ return this._year; }, set: function(newValue){ if(newValue>2004){ this.year= newValue; this.edition += new Value-2004; } } }); book.year = 2005; alert(book.edition); //2
上例创建了一个book对象,并定义了两个默认属性 _year 和 edition 。_year前边的下划线表示只能通过对象方法访问的属性。而访问器属性year则包含getter和setter函数。修改year属性,会导致_year和edition改变。这就是访问器属性的常用方式,即设置一个属性值会导致其他属性发生变化。
当然,不一定非要同时制定getter和setter,只有getter则不能写,只有setter则不能读。
发布/订阅模式的介绍
发布/订阅模式是设计模式的一种,很多地方把他和观察者模式混为一谈,其实他们是有一些区别的。对于设计模式,其实目前我的了解其实也非常有限,但如果只是想搞清楚这一种模式的原理,其实还是很容易的。简单来说,发布/订阅模式就是他的字面意思:他定义了一种一对多的关系,让多个订阅者同时关注发布者,当发布者状态改变时,会通知所有订阅他的对象。就好比在B站追番,点了订阅之后,就成为了该番的订阅者,当动画更新的时候,就会发布消息,通知所有的订阅者,然后你就可以去新的一集了。
在Vue中的作用
Vue会遍历实例的data属性,把每一个data都设置为访问器,然后在该属性的getter函数中将其设为watcher,在setter中向其他watcher发布改变的消息。这样,配合发布/订阅模式,改变其中的一个值,会发布消息,所有的watcher会更新自己,这些watcher也就是绑定在dom中的显示信息,比如 v-text=”year” 和 {{ year }} 这些节点。从而达到改变浏dom,在浏览器中实时变化的效果,代码如下://遍历传入实例的data对象的属性,将其设置为Vue对象的访问器属性 function observe(obj,vm){ Object.keys(obj).forEach(function(key){ defineReactive(vm,key,obj[key]); }); } //设置为访问器属性,并在其getter和setter函数中,使用订阅发布模式。互相监听。 function defineReactive(obj,key,val){ //这里用到了观察者(订阅/发布)模式,它定义了一种一对多的关系,让多个观察者监听一个主题对象,这个主题对象的状态发生改变时会通知所有观察者对象,观察者对象就可以更新自己的状态。 //实例化一个主题对象,对象中有空的观察者列表 var dep = new Dep(); //将data的每一个属性都设置为Vue对象的访问器属性,属性名和data中相同 //所以每次修改Vue.data的时候,都会调用下边的get和set方法。然后会监听v-model的input事件,当改变了input的值,就相应的改变Vue.data的数据,然后触发这里的set方法 Object.defineProperty(obj,key,{ get: function(){ //Dep.target指针指向watcher,增加订阅者watcher到主体对象Dep if(Dep.target){ dep.addSub(Dep.target); } return val; }, set: function(newVal){ if(newVal === val){ return } val = newVal; //console.log(val); //给订阅者列表中的watchers发出通知 dep.notify(); } }); } //主题对象Dep构造函数 function Dep(){ this.subs = []; } //Dep有两个方法,增加观察者 和 发布消息 Dep.prototype = { addSub: function(sub){ this.subs.push(sub); }, notify: function(){ this.subs.forEach(function(sub){ sub.update(); }); } }
Object.defineProperty 是仅 ES5 支持,且无法 shim 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器的原因。
结合documentFragment,defineProperty和发布/订阅模式,就可以简单的实现一个双向绑定了,当然这只是最基本的思路,实际上vue还要处理要复杂的多。
相关文章推荐
- Vue 双向数据绑定原理分析 以及 Object.defineproperty语法
- Vue双向绑定的关键:Object.defineProperty()
- Vue.js双向绑定内核—Object.defineProperty解析
- vue.js利用defineProperty实现数据的双向绑定
- JavaScript设计模式系列05_观察者模式(发布订阅)写的数据联动(类似于vue的数据绑定)
- Object.defineProperty()/ vue数据绑定原理
- 应用defineProperty简单实现vue的双向数据绑定
- Vue源码解读——实现一个双向绑定(Object.defineProperty与observe)
- vue双向数据绑定原理探究(附demo)
- Vue 双向绑定的原理及实现Demo
- 剖析Vue原理&实现双向绑定MVVM
- vue双向数据绑定的原理解密
- Vue.js双向绑定的实现原理
- 使用Object.defineProperty实现简单的js双向绑定
- 初探Vue原理之view-model的数据动态双向绑定
- vue数据双向绑定原理解析(get & set)
- Vue2.0源码阅读笔记--双向绑定实现原理
- Vue双向数据绑定原理分析
- Vue.js双向绑定的实现原理
- vue双向数据绑定原理探究(附demo)