参考:http://www.cnblogs.com/canfoo/p/6891868.html
vue实现双向数据绑定的原理是利用了Object.defineProperty()这个方法重新定义对象获取属性get()方法和设置属性set()方法来实现数据劫持。
Object.defineProperty(obj,prop,descriptor)
- obj 要在其上定义属性的对象
- prop 要定义或修改的属性的名称
- descriptor 将要被定义或修改的属性描述符
在平常,我们可以很容易的打印出一个对象的属性数据
var book = {
name: 'JavaScript高级程序设计'
}
console.log(book.name); //JavaScript高级程序设计
那么如果想要在执行console.log(book.name)的同时,直接给书名加个书名号,要怎么处理呢?或者说要通过什么监听对象Book的属性值。
这时候就要运用到Object.defineProperty()
var Book = {};
var name = '';
Object.defineProperty(Book,'name',{
set: function(value){
name = value;
console.log('这本书的名字叫' + value); //这本书的名字叫JavaScript高级程序设计
},
get: function() {
return '《'+ name +'》'
}
})
Book.name = 'JavaScript高级程序设计';
console.log(Book.name);//《JavaScript高级程序设计》
访问属性时,执行get()方法;修改/设置属性时,执行set()方法。由于我们在get()方法里return的时候加上了书名号,所以在console.log(Book.name)时该书名就会带上书名号
想要实现mvvm,包含两个方面:数据变化更新视图,视图变化更新数据。
那么当数据更新时,要如何更新视图呢?首先我们要思考 如何知道数据变了,由以上例子可知,可以通过Object.defineProperty()对属性设置set()函数,当数据改变时会触发该方法。
现在,我们来实现数据双向绑定
- 实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
- 实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。
- 实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。
1、实现一个监听器Observer
function defineReactive(data, key, val) {
observe(val); // 递归遍历所有子属性
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function() {
return val;
},
set: function(newVal) {
val = newVal;
console.log('属性' + key + '已经被监听了,现在值为:“' + newVal.toString() + '”');
}
});
}
function observe(data) {
if (!data || typeof data !== 'object') {
return;
}
Object.keys(data).forEach(function(key) {
defineReactive(data, key, data[key]);
});
};
var library = {
book1: {
name: ''
},
book2: ''
};
observe(library);
library.book1.name = 'vue权威指南'; // 属性name已经被监听了,现在值为:“vue权威指南”
library.book2 = '没有此书籍'; // 属性book2已经被监听了,现在值为:“没有此书籍”
…