上篇文章讲解了 Object.defineProperty() 方法现在我们用它来将一个简单对象变成响应式的。 getter可以监测对象的属性被访问了,setter可监测对象的属性被改变了,我们通过这两个方法就可以监测对象的变化。
let obj = {};
Object.defineProperty(obj,"name",{
get(){
console.log("你访问了name属性");
return "小明";
},
set(newValue){
console.log("你试图修改name属性");
}
})
console.log(obj.name) // 输出 "小明" 还会输出你访问了name属性
obj.name = "小红" // 会输出:你试图修改name属性
这里存在一个问题 当你执行完上面的代码 再去打印obj.name时 会发现 结果依然是 输出 "小明" 和 "你访问了name属性"。obj.name = "小红"; 并不能真的改变obj.name 的值。 这是因为set 方法虽然可以接收对象属性的新值但并不能改变该属性的值,get方法的返回值才会被赋值给对象该属性。下面要做的就很简单了
let obj = {}, value = "小明";
Object.defineProperty(obj,"name",{
get(){
console.log("你访问了name属性");
return value;
},
set(newValue){
value = newValue;
console.log("你试图修改name属性");
}
})
console.log(obj.name) // 输出 "小明" 还会输出你访问了name属性
obj.name = "小红" // 会输出:你试图修改name属性
console.log(obj.name) // 输出 "小红" 还会输出你访问了name属性
这样我们就可以正常的修改该对象的值,并且能监测到该对象的变化了,但是这样通过外部变量的方式似乎不够优雅,我们可以通过更优雅的闭包来决绝。让我们封装一个方法:
/* 将一个对象变成响应式的
*@param { object } data 你要定义的对象
*param { string } key 你要定义的属性名
*@param { * } val 该属性的值
*/
function defineReactive (data,key,val){
Object.defineProperty(data,key,{
// 可枚举
enumerable:true,
// 可以被配置
configurable:true,
//getter
get(){
console.log("你访问了"+key+"属性");
return val;
},
//setter
set(newValue){
val = newValue;
console.log("你试图修改"+key+"属性");
}
})
}
这样当你再想定义一个响应式的属性时:
let obj = {};
defineReactive(obj,"name","小明");
在实际应用场景中我们所要定义响应式的对象往往不是空的,所以我们的defineReactive方法还应该能够把对象中已存在的属性变成响应式的,这并不困难:
/* 将一个对象变成响应式的
*@param { object } data 你要定义的对象
*param { string } key 你要定义的属性名
*@param { * } val 该属性的值
*/
function defineReactive (data,key,val){
if(arguments.length == 2){
val = data[key];
}
Object.defineProperty(data,key,{
// 可枚举
enumerable:true,
// 可以被配置
configurable:true,
//getter
get(){
console.log("你访问了"+key+"属性");
return val;
},
//setter
set(newValue){
val = newValue;
console.log("你试图修改"+key+"属性");
}
})
}
现在当你想把一个对象变成响应式时只需要:
let obj ={
name:"小明",
age:21
};
for (let key in obj) {
defineReactive(obj,key)
}