Vue源码学习-监听对象的变化
Vue的源码直接看起来还是很费劲,还好这方面的资料也很多。这篇文章写写Vue最基本的核心 — 基于Object.defineProperty
对getter
和setter
函数的劫持,实现对数据变化的监听。
思路其实是很容易理解的,通过自定义访问器属性中的getter
和setter
函数来实现对属性的监听。
提到getter
和setter
就要说一说对象的访问器属性和数据属性,数据属性只有基本的四个特性:
- [[configurable]] 可配置
- [[enumerable]] 可枚举
- [[writable]] 可写
- [[value]] 值
当访问这一属性是,可以直接获取到[[value]]
的值,同理修改时也是修改的这一值。而不同于数据属性,访问器的四个属性则是:
- [[configurable]]
- [[enumerable]]
- [[getter]]
- [[setter]]
通过get和set方法来获取和修改值,在JS中我们可以直接通过Object.defineProperty
这一接口来自定义这两个方法以达成监听的目的:
// 最简单的情况,针对值为简单类型的属性
var person = {
name: 'jack'
}
person.listen = function(property) {
var val = this[property]
Object.defineProperty(this, 'name', {
get: function () {
console.log('oops someone is trying to get the value')
return val
},
set: function (newVal) {
console.log('ok the name now has been changed to ' + newVal)
val = newVal
}
})
}
可以看下面的GIF查看效果:
但是如果name的值是一个对象,修改这个对象中的值并不会改变 name所保存的对象引用,因此也就无法达成监听的目的,解决的方法就是利用递归算法:
// 将数据多包装一层
var Observer = function(data) {
this.data = data
this.iterate(data)
}
// 遍历每一个属性
Observer.prototype.iterate = function(obj) {
var val
for (var key in obj) {
val = obj[key]
// 如果属性对应的值为对象,继续递归遍历
if (typeof val === 'object') {
new Observer(val)
}
// 监听属性
this.listen(key, val)
}
}
// 监听属性函数
Observer.prototype.listen = function(key, value) {
Object.defineProperty(this.data, key, {
get: function () {
console.log('oops someone is trying to get the value of ' + key)
return value
},
set: function (newVal) {
console.log('ok the ' + key + ' now has been changed to ' + newVal)
// 如果值设置为了新的对象,重新遍历监听
if (typeof newVal === 'object') {
new Observer(newVal)
}
value = newVal
}
})
}
var person = new Observer({
name: 'jack',
age: 25,
info: {
prof: 'teacher',
city: 'beijng'
}
})
结果如图:
下一篇我们说说如何监听一个数组的问题。