Object的defineProperty方法
可以使用Object.deefineProperty为一个对象添加一个属性:
let person = {name: '张三',sex: '男'}// 为person对象添加一个age属性,值是18// Object.defineProperty方法的参数:// 参数1:要操作的对象// 参数2:对象属性名// 参数3:配置项Object.defineProperty(person, 'age', {value: 18});console.log(person);
添加上来的age属性,在浏览器控制台显示的颜色比较淡。该属性不能枚举(不参与遍历):
// 遍历person对象的所有属性,只能遍历到 name、sex,不能遍历到ageconsole.log(Object.keys(person));// 使用for循环也不能遍历到agefor (const key in person) {console.log(key + ":" + person[key]);}
如果想要添加进来的age属性可以枚举,需要在设置时添加另外一个配置项:
Object.defineProperty(person, 'age', {value: 18,enumerable: true // 控制属性是否可以枚举,默认值false});
其他的基本配置项:
value:属性的值enumerable:是否可以枚举,默认falsewritable:是否可以被修改,默认falseconfigurable:是否可以被删除,默认false
Object.defineProperty(person, 'age', {value: 18, // 设置属性值为18enumerable: true, // 可以枚举writable: true, // 可以修改configurable: true // 可以删除});
高级用法:
属性值是一个变量,需要根据变量的变化而变化。
let num = 18;let person = {name: '张三',sex: '男',age: num};console.log(person);num = 19;// 此时,虽然num改成了19,但是person.age还依然是18console.log(person);
使用Object.defineProperty实现:
let num = 18;let person = {name: '张三',sex: '男'};Object.defineProperty(person, 'age', {// 当读取person的age属性时,get就会被调用,且返回值就是age的值get:function() {// 可以简写为get(){console.log('有人读取age属性了');return num;}})// 浏览器控制台中,age属性为invoke property getter// 控制台显示对象的属性中有一个为age属性服务的getter// 不点击age的值时,age值不显示,只有点击(读取)时才会调用getter获取age的值console.log(person);console.log(person.age); // age会读取此时的num值18num = 19;console.log(person.age); // age会读取此时的num值19
除了可以设置getter外,可以设置setter:
let num = 18;Object.defineProperty(person, 'age', {get() {console.log('有人读取age属性了');return num;}// setter方法// 当有人修改person的age属性时,setter就会被调用,且会收到修改的值set(value) {console.log('age被修改了:' + value);num = value ; // 将修改后的值赋给对象属性}})
数据代理
数据代理:通过一个对象,代理另外一个对象中属性的操作(读/写)。
例如:通过obj2操作obj1的x属性
let obj1 = {x: 100};let obj2 = {y: 200};Object.defineProperty(obj2, 'x', {get(){return obj1.x;},set(value) {obj1.x=value;}})
在Vue中,构造出的vm对象data中的变量,也是通过数据代理实现的:
const vm = new Vue({data: {name: 'tom',age: 18}});console.log(vm);
在控制台查看vm的name、age属性,是通过数据代理加进来的,有自己的getter/setter。
当读取vm的name属性时,会调用name的getter,getter方法将data的name读取出来;
当修改vm的name属性时,会调用name的setter,setter方法将data的name值修改。
vm会将编码的data存储在vm._data中:
let data = {message: "test"};const vm = new Vue({el: '#app',data: data});// vm._data和data完全相等console.log(vm._data === data);console.log(vm._data.message); // 可以正常输出,所以vm.message取出来的实际是vm._data.message
