Vue正是使用了它实现了响应式数据功能
Vue中定义响应式数据的代码:
export function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
.....
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
if (Array.isArray(value)) {
dependArray(value)
}
}
}
return value
},
set: function reactiveSetter (newVal) {
const value = getter ? getter.call(obj) : val
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter()
}
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = !shallow && observe(newVal)
dep.notify()
}
})
}
Object.defineProperty
Object.defineProperty这个函数传递了三个参数
第一个参数代表要设置的对象,第二个参数代表要设置的对象的键值,第三个参数是一个配置对象,对象里面可以设置参数如下:
**value**: 对应key的值
**configurable**:是否可以删除该key或者重新配置该key
**enumerable**:是否可以遍历该key
**writable**:是否可以修改该key
**get**: 获取该key值时调用的函数
**set**: 设置该key值时调用的函数
我们通过例子来了解一下这些属性
let people = {}
people['name'] = '张三'
console.log(Object.getOwnPropertyDescriptor(people,'name'))
Object.getOwnPropertyDescriptor可以获取对象某个key的描述对象,打印结果如下:
{
value: "张三", writable: true, //改写
enumerable: true, //设置
configurable: true //遍历
}
从上可知,该key对应的属性我们可以改写、可以重新设置或者删除、可以遍历。我们试试来修改一下这些属性,
试试**configurable,**代码如下:
Object.defineProperty(people, 'name', {
configurable: false
})
执行上述代码成功后,将无法重新设置或删除name属性,比如delete people['name'],你会发现返回为false,即无法删除了。
试试**enumerable,**代码如下:
let people = {}
people["name"] = "张三"
people["age"] = 25
Object.defineProperty(people, "age", {
enumerable: false
})
for(let key in people){
console.log("key:" + key + "————value:" + people[key])
}
打印结果为
key:name——value:张三
为什么只打印了name没有打印age?
因为我们将age设置了不可遍历(enumerable:false),那我们的for循环就取不到了,但是我们用people[“age”]能取到
Observer类中有下面一行代码:
def(value, '__ob__', this);
def是个工具函数,目的是想给value添加一个key为__ob__,值为this,为什么不直接value.__ob__ = this ?
因为程序下面要遍历value对其子内容进行递归设置,如果直接用value.__ob__这种方式,在遍历时会取到,而我们是想它遍历时无法取到,所以def函数是利用Object.defineProperty给value添加属性,同时将enumerable设置为false。
**get和set?**
类似于在获取对象值和设置对象值时加了一个代理,当值发生改变时通知View视图更新。
let people = {}
Object.defineProperty(people, "name", {
get: function(){
console.log("getter called!")
},
set: function(newVal){
console.log("setter called! newVal is:" + newVal)
}
})
当我们访问people["name"]时便会打印getter called,当我们设置people["name"]="李四"时,打印setter called!newVal is 李四。Vue源码通过这种方式实现了访问属性时收集依赖,设置属性时View视图更新,源码里有一句dep.notify,里面便是通知视图更新的相关操作。