对象变化侦测原理(一)
变化侦测原理
一、对象属性
对于 js 对象的属性,可以分为2种:数据属性和访问属性。
1.1 数据属性
数据属性就是普通的对象属性值,可以读入和写入值。
例如,下述的属性 data 就是数据属性:
1 | var obj = {}; |
对于数据属性,它有4个特性:
- [[Configureable]]:表示能否通过
delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问属性。 - [[Enumerable]]:表示能否通过 for-in 循环返回属性。
- [[Writable]]:表示能否修改数据的值。
- [[Value]]:这个属性的值。
这些属性特性一般是不能通过一般的方式获取到,需要通过使用方法 Object.getOwnPropertyDescriptor(obj, key)来获取特性值。以上面的代码为例:
1 | Object.getOwnPropertyDescriptor(obj, 'data') |
1 | { |
通过一般设置的对象属性,默认都是数据属性,而除了 value 特性以外,其他特性的值都是默认为 true。
如果需要修改特性的值,必须使用方法 Object.defineProperty(obj, key, option)来修改。
1 | Object.defineProperty(obj, 'data', { |
再查看属性的特性,此时 writable 特性的值就已经发生变化了。
1 | Object.getOwnPropertyDescriptor(obj, 'data') |
1 | { |
writable=false 表示属性不能修改,因此修改属性值是不生效的。
1 | alert(obj.data) // "data" |
可以多次调用 Object.defineProperty 来修改属性特性。
接下来看一下特性 configurable 的作用:
1 | Object.defineProperty(obj, 'data', { |
configurable=false 表示属性无法通过 delete 删除,再重新定义属性。
1 | alert(obj.data) // "data" |
而且,还有一个比较重要的点,那就是一旦把 configurable 设置为 false 之后,就不能再把它重新恢复为 true 了。
1 | Object.defineProperty(obj, 'data', { |
1.2 访问属性
访问属性不包括数据,实际上是通过一对函数 setter 和 getter 进行属性的读取访问和设置。
访问属性不能通过一般的对象属性进行定义,必须使用 Object.defineProperty(obj, key, option) 进行定义。
1 | // 数据属性 |
属性 access 就是所谓的访问属性,它和一般的数据属性 data 一样,它也有 4 个特性:
- [[Configureable]]:表示能否通过
delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问属性。 - [[Enumerable]]:表示能否通过 for-in 循环返回属性。
- [[Get]]:在读取属性时调用的函数。
- [[Set]]:在设置属性时调用的函数。
其中特性 Configureable 和 Enumerable 和数据属性中的是一样的。
获取属性特性的值也和数据属性的一样,通过方法 Object.getOwnPropertyDescriptor(obj, key) 来获取:
1 | Object.getOwnPropertyDescriptor(objProxy, 'access') |
1 | { |
访问属性比较特别,可以通过在访问函数 setter 和 getter
中做一些中间操作,获得其他的效果:
1 | var readTimes = 0 |
这种利用访问函数的方式,其实就是 Vue 定义响应式属性所用的方法,通过利用访问属性的 setter 和 getter 来收集和通知属性发生了变更,就可以实现响应式属性。
也许,访问属性叫访问方法更好,虽然操作上和数据属性类似,但是它本身并不算一个属性,只是通过方法 setter 和 getter 进行了定义,然后拿的还是其他数据属性的值。
定义 setter 和 getter 实际上就是在定义 objProxy.access= 和 objProxy.access。调用 objProxy.access 就是在调用 getter 函数,调用 objProxy.access= 就是在调用 setter 函数。