// 部分重复代码 这里就不再罗列了。
function defineReactive (obj, key) {
// ... const desc = Object.getOwnPropertyDescriptor(obj, key)
if (desc && desc.configurable === false) {
return
}
// ...
}
而在 JavaScript 中,导致 configurable 值为 false 的情况还是很多的:
可能该属性在此之前已经通过 Object.defineProperty() 方法设置了 configurable 的值;
通过 Object.seal() 方法设置该对象为密封对象,只能修改该属性的值并且不能删除该属性以及修改属性的描述符;
通过 Object.freeze() 方法冻结该对象,相比较 Object.seal() 方法,它更为严格之处体现在不允许修改属性的值。
2、默认 getter 和 setter 方法
另外,开发者可能已经为对象的属性设置了 getter 和 setter 方法,对于这种情况,Vue 当然不能破坏开发者定义的方法,所以 Vue 中还要保护默认的 getter 和 setter 方法:
// 部分重复代码 这里就不再罗列了
function defineReactive (obj, key) {
let val = obj[key] //....
// 默认 getter setter
const getter = desc && desc.get
const setter = desc && desc.set
Object.defineProperty(obj, key, {
get () {
const value = getter ? getter.call(obj) : val // 优先执行默认的 getter
return value
},
set (newValue) {
const value = getter ? getter.call(obj) : val
// 如果值相同则没必要更新 === 的坑点 NaN!!!!
if (newValue === value || (value !== value && newValue !== newValue)) {
return
}
if (getter && !setter) {
// 用户未设置 setter
return
}
if (setter) {
// 调用默认的 setter 方法
setter.call(obj, newValue)
} else {
val = newValue
}
}
})
}
3、递归属性值
最后一种比较重要的情况就是属性的值可能也是一个对象,那么在处理对象的属性时,需要递归处理其属性值:
function defineReactive (obj, key) {
let val = obj[key] // ...
// 递归处理其属性值
const childObj = observe(val)
// ...
}
递归循环引用对象很容易出现递归爆栈问题,对于这种情况,Vue 通过定义 ob 对象记录已经被设置过 getter 和 setter 方法的对象,从而避免递归爆栈的问题。
function isObject (val) {
const type = val
return val !== null && (type === 'object' || type === 'function')
}function observe (value) {
if (!isObject(value)) {
return
}
let ob
// 避免循环引用造成的递归爆栈问题










