对象变化侦测原理(一)
变化侦测原理
一、对象属性
对于 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
函数。