Object.defineProperty()是Vue中比较底层的一个方法,在数据劫持,数据代理以及计算属性等地方都或多或少的有用到,理解数据代理,需要先熟悉Object.defineProperty()用法。
一.回顾Object.defineProperty用法
Object.defineProperty() 是 JavaScript 中用于定义或修改对象的属性的方法,可以控制属性的特性(如可枚举性、可配置性、可写性等)。
1.Object.defineProperty() 语法:
Object.defineProperty(obj, prop, descriptor)
2.参数说明:
obj: 要定义属性的对象。prop: 要定义或修改的属性的名称。descriptor:属性的描述符对象。
3.descriptor 对象包含以下属性
value:属性的值,默认为 undefinedenumerable:控制属性是否可以被枚举,默认值是falsewritable:控制属性是否可以被修改,默认值是falseconfigurable:控制属性是否可以被删除,默认值是falseget:获取属性值的函数set:设置属性值的函数
4.案例
1)给对象增加属性
需求:给person对象增加age属性,值为18
let person = {
name: "张三",
sex: "男",
// age:18, //直接在对象里面写可以被枚举
};
Object.defineProperty(person,'age',{
value:18 //属性的值,默认为 undefined
})
console.log(person); //控制台输出的age颜色更浅,表示age不可以被枚举的,即不参与遍历
console.log(Object.keys(person)) //不输出age
2)修改对象属性的可枚举性、可配置性、可写性
需求:修改person对象的age属性特性
Object.defineProperty(person,'age',{
value:18,//属性的值,默认为 undefined
enumerable: true, //控制属性是否可以被枚举,默认值是false
writable: true, //控制属性是否可以被修改,默认值是false
configurable: true, //控制属性是否可以被删除,默认值是false
})
Object.defineProperty看起来麻烦,但是可以对追加的属性有更多的限制。
3)读取/修改对象的值
需求:给person增加属性age,且age的取值为number的值。
let number = 18;
let person = {
name: "张三",
sex: "男",
// age: number, //用这种方法赋值后,number的值改变时,age不会自动变化
};
Object.defineProperty(person, "age", {
//当有人读取person的age属性值时,get函数(getter)就会被调用,且返回值就是age的值
get() {
console.log("有人读取person的age属性值");
return number; //用这种方法赋值后,number的值改变时,age也会自动变化
},
//当有人修改person的age属性值时,set函数(setter)就会被调用,且会收到修改的具体值
set(value) {
console.log("有人修改age属性,值是" + value);
//直接 person.age=10; 不会改变person里面age的值,需要同步改变number的值
number = value;
},
});
console.log(person);
注意:当配置项当中有setter和getter的时候,value和writable配置项都不能存在,否则报错。
当有人读取对象的属性值时,get函数(getter)就会被调用,且返回值就是属性值。
当有人修改对象的属性值时,set函数(setter)就会被调用,且会收到修改的具体值。
二.何为数据代理?
数据代理:通过一个对象代理对另一个对象中属性的操作(读/写) 一个简单的数据代理案例
let obj1 = { x: 1 };
let obj2 = { y: 2 };
Object.defineProperty(obj2, "x", {
get() {
return obj1.x;
},
set(value){
obj1.x=value;
}
});
这个案例中obj2代理obj1,通过操作obj2可以读取obj1的属性值,也可以对obj1的属性赋值,这就是数据代理。
三.vue中的数据代理
<div id="app">
<h3>年份:{{year}}</h3>
</div>
<script type="text/javascript">
//创建vue实例
const vm = new Vue({
data() {
return {
year: 2025,
};
},
});
vm.$mount("#app");
console.log(vm);
</script>
在控制台打印vm,发现year有get和set方法,初步猜测vue中有数据代理,需要验证。
如果验证下图成立,则证明有数据代理:
1.getter路径:修改data.year,vm.year会跟着变化。证明当读取vm的year值时,实际取的是data中的year值。
2.setter路径:vm.year发生修改,data.year会跟着变化。证明当修改vm的year值时,实际上修改的是data中的year值。
总结:
1.vue中的数据代理:通过vm对象来代理data对象中属性的操作(读/写)
2.vue中数据代理的好处:更加方便的操作data中的数据,不用再写vm._data.year
3.基本原理通过Object.defineProperty()把data对象中的所有属性添加到vm上。 为每一个添加到vm上的属性,都指定一个getter/setter。 在getter/setter内部去操作(读/写)data中对应的属性。
如图解释vue中的数据代理: