Object.defineProperty大家都不陌生,废话不多说,从Vue源码开始,已删除部分关联不大的代码
/**
* Define a reactive property on an Object.
*/
export function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: ?Function,//非必要参数
shallow?: boolean//非必要参数
) {
//此处的property会在下面做解释
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
const getter = property && property.get
const setter = property && property.set
//Object.defineProperty 的set和get属性在没有处理前,默认值是undefined
//arguments.length === 2也就意味着没有传入val参数
if ((!getter || setter) && arguments.length === 2) {
val = obj[key]
}
Object.defineProperty(obj, key, {
get: function reactiveGetter () {
//注意,上面已经说明,getter默认值是undefined
const value = getter ? getter.call(obj) : val
return value
},
set: function reactiveSetter (newVal) {
//注意,上面已经说明,setter默认值是undefined
const value = getter ? getter.call(obj) : val
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
if (getter && !setter) return
if (setter) {
setter.call(obj, newVal)
} else {
//注意,此处,下面会做解释
val = newVal
}
}
})
}
问题1:
const property = Object.getOwnPropertyDescriptor(obj, key)是什么?
简洁的说,Object.getOwnPropertyDescriptor()
方法返回指定对象上一个自有属性对应的属性描述符。详细了解请看这
let data = {
form:{
name: 'ShaneHu',
sex: 'male'
}
}
let descriptor = Object.getOwnPropertyDescriptor(data, 'form');
console.log(descriptor)
以上代码执行结果:
{
"value": {
"name": "ShaneHu",
"sex": "male"
},
"writable": true,
"enumerable": true,
"configurable": true
}
用Object.defineProperty处理data对象后:
let data = {
form:{
name: 'ShaneHu',
sex: 'male'
}
}
function observe(data, key) {
//声明内部变量
let value = data[key]
Object.defineProperty(data, key, {
get() {
return value
},
set(newValue) {
if (value === newValue) return;
value = newValue
}
})
}
observe(data,'form')
let descriptor = Object.getOwnPropertyDescriptor(data, 'form');
console.log(descriptor)
以上代码执行结果:
{
"enumerable": true,
"configurable": true,
"get": get(),
"set": set(newValue)
}
所以,const value = getter ? getter.call(obj) : val
返回的其实是前面赋值过的 obj[key]。
//Object.defineProperty 的set和get属性在没有处理前,默认值是undefined
//arguments.length === 2也就意味着没有传入val参数
if ((!getter || setter) && arguments.length === 2) {
val = obj[key]
}
那么,问题2:
为什么是val=newVal,而不是obj[key]=newVal
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
起初,我写Object.defineProperty
的时候,是直接在set方法里面,直接obj[key]=newVal,结果自然是报错的。
以下是我根据Vue源码改写的简版数据劫持,便于理解:
function observe(data, key, val) {
//声明内部变量
let value = val || data[key];
Object.defineProperty(data, key, {
get() {
return value
},
set(newValue) {
if (value === newValue) return;
value = newValue
}
})
}
1.这个方法是一个闭包,value是一个函数内的局部变量;
2.set方法,我们可以理解为不直接修改data对象下某个属性的值,而是修改value这个局部变量的值;
3.因此,get方法,返回的也不是data对象下某个属性的值,而是value这个局部变量;