1.定义
根据View与Model之间的关系,可以分为单向绑定和双向绑定,本文中的双向绑定的概念为当V改变时,M也跟着改变,当M改变时,V也跟着改变
实现双向绑定,一般我们需要定义Observe,Watch,Compiler,本文为第一部分,讲解怎么去实现一个数据监听
2.Object.defineProperty
如何去实现数据监听,主要的思路就是当获取值或者设置时,我们能知道,Object正好有这个属性,我们来看看定义
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象
Object.defineProperty(obj, prop, descriptor)
2.1 obj
这是第一个参数,要定义的对象
2.2 prop
要定义的属性
2.3 descriptor
该属性具有的特性
- 2.3.1 configurable
当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false
这句话的意思是,假如为false,那么假如已经使用definePropery来定义了,就不能再定义了,或者不能用delete删除,看代码
var obj = {}
Object.defineProperty(obj, 'key1', {
value: 'test',
configurable: false
})
Object.defineProperty(obj, 'key1', {
value: 'test2',
writable: false
})
此时就会报下面的错误,不允许重新定义
我们把代码改成下面的,再执行删除试试
var obj = {}
Object.defineProperty(obj, 'key1', {
value: 'test',
configurable: false
})
delete obj.key1
此时去打印obj.key1时,仍然能够打印test,假如加上use strict,还会报错
"use strict"
var obj = {}
Object.defineProperty(obj, 'key1', {
value: 'test',
configurable: false
})
delete obj.key1
console.log(obj.key1)
- 2.3.2 enumerable
代表该属性是否可枚举,关于属性的枚举属性,请参考这里,一个属性是否能被枚举,本文用的Object.keys,来看代码
var obj = {}
Object.defineProperty(obj, 'key1', {
value: 'test',
enumerable: true
})
console.log(Object.keys(obj))
此时,打印的结果如下
假如把enumerable改为false,结果如下
- 2.3.3 value
该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined,这里要注意的是,假如定义了value,就不能定义set或者get - 2.3.4 writable
代表该值是否可写
"use strict"
var obj = {}
Object.defineProperty(obj, 'key1', {
value: 'test',
writable: false
})
此时修改key1是无用的,但是不会报错
- 2.3.5 set get
这两个属性用来一起说明吧,看官网的定义有点生涩,我们用来理解成,当读取这个属性时,调用get,给这个属性设置值时,调用set,这是不是已经说出了我们定义观察者的基本思路了,接下来我们就根据Object的这个属性来定义我们的Observe
3.Observe
我们怎么去实现一个观察者呢,基本的思路就是,每个属性我们都用defineProperty,设置set和get,那么在读取属性和设置属性时,我们都能监听到。根据这个思路,使用递归是肯定的了,我们一层层地去遍历对象,直到没有子对象为止。
思路有了, 我们来看代码吧
function Observe(obj) {
if (!obj || Object.prototype.toString.call(obj).toLowerCase() != '[object object]') {
return
}
Object.keys(obj).forEach((item) => {
// 取值,并且把这个值存起来,要是在defineProperty后再取值,会陷入死循环
var value = obj[item]
Object.defineProperty(obj, item, {
set: (newValue) => {
console.log('set value')
if (value == newValue) {
return value
} else {
value = newValue
return newValue
}
},
get: () => {
console.log(`get key:${item},value:${value}`)
return value
}
})
// 继续去给当前的这个对象进行遍历
return Observe(value)
})
}
上面的代码,是不是很简单,不超过20行就实现了一个观察者,在本文中,我们仅仅用到了set和get方法,去除Object.defineProperty的理论知识,干货真的不多,但却实现了我们双向绑定的最核心的部分