1、作用
顾名思义,Object.defineProperty
可以定义一个对象,但这不仅仅是它的最主要功能,它的主要功能在于他可以配置定义的属性,也可以使用set
,get
做数据劫持,实现响应式数据,vue2中则是利用它实现了更新数据,即可更新dom
的响应式原理。
2、用法
Object.defineProperty(obj, property, descriptor)
有三个属性:
-
第一个参数:
obj
是要定义属性的原对象 -
第二个参数:
property
是定义的属性名或者symbol
-
第三个参数:要定义属性的描述配置
- configurable: 可配置属性,控制
descriptor
是否可以被重新改变和obj
定义属性的删除,默认值为false
- enumerable:可枚举属性,控制对象的
property
是否可遍历,默认为false
- writable:是否可修改,控制属性是否可以被赋值运算符修改,默认为
false
- value:属性的初始值,默认为
undefined
- set:
setter
函数,可以拦截obj.prop = 'xxx'
的操作,在其中做一些逻辑处理 - get:
getter
函数,可以拦截obj.prop
的操作,在读取运算中做一些逻辑处理
- configurable: 可配置属性,控制
-
返回值:返回定义属性和添加描述配置后的
obj
3、例子
configurable:
configurable
如果首次定义为false
,那么它会禁止在此修改此属性,一旦定义就无法修改,只有首次定义为true
时候,可以在此修改。此外,只有此属性为true
时,才可以删除定义的属性。
// 例1
const obj = {};
Object.defineProperty(obj, "name", {
// 默认为false 可以不写,这里只是用例
configurable: false,
value: 'jack'
});
console.log(obj.name); // 'jack'
// 下面修改报错 Cannot redefine property: name
Object.defineProperty(obj, "name", {
configurable: true,
value: "tom"
});
// 例2
const obj = {};
Object.defineProperty(obj, "name", {
// 默认为false 可以不写,这里只是用例
configurable: true,
value: 'jack'
});
console.log(obj.name); // 'jack'
delete obj.name;
console.log(obj.name); // undefined
// 可以再次修改配置
Object.defineProperty(obj, "name", {
value: "tom"
});
console.log(obj.name);// 'tom'
enumerable:
控制该属性是否可以被遍历,是否可以在for..in..
和Object.keys()
中枚举
// 例1
const obj = {
age: 20,
gender: "男",
address: "北京"
};
Object.defineProperty(obj, "name", {
// 当设置可枚举为true
enumerable: true,
value: 'jack'
});
console.log(Object.keys(obj)); // [ 'age', 'gender', 'address', 'name' ]
for (key in obj) {
console.log(key); // 'age', 'gender', 'address', 'name'
}
// 例2
const obj = {
age: 20,
gender: "男",
address: "北京"
};
Object.defineProperty(obj, "name", {
enumerable: false,
value: 'jack'
});
console.log(Object.keys(obj)); // [[ 'age', 'gender', 'address' ]
for (key in obj) {
console.log(key); // 'age', 'gender', 'address'
}
writable:
控制定义的属性是否可以重新赋值
// 例1
const obj = {};
Object.defineProperty(obj, "name", {
writable: true,
value: 'jack'
});
console.log(obj.name); // 'jack'
obj.name = "tom";
console.log(obj.name);// 'tom'
// 例2
const obj = {};
Object.defineProperty(obj, "name", {
writable: false, // 将可修改属性设为false
value: 'jack'
});
console.log(obj.name);// 'jack'
obj.name = "tom";
console.log(obj.name);// 'jack'
set 和 get:
设置setter
函数,拦截属性赋值操作,设置getter
函数可以拦截属性值的读取。
⚠️注意:如果一个描述符同时拥有 value
或 writable
和 get
或 set
键,则会产生一个异常。会报错: Cannot both specify accessors and a value or writable attribute
,不能同时指定访问器和值或可写属性。
⚠️注意:set
函数中this
的指向是赋值的对象,由于继承关系,get
函数里面的this
不一定指向定义的对象。
// 创建一个新的对象 参数作为新对象的__proto__
const proto = {
age: 20
};
const obj = Object.create(proto);
console.log(proto); // { age: 20 }
Object.defineProperty(obj.__proto__, "name", {
set: (val) => {
console.log("被赋值了!!!");
this.name = val;
},
get: () => {
console.log("被读取了!!!");
return this.name
}
});
obj.name = "jack"; // obj继承了proto的属性和方法 所以obj的改变可以触发set函数执行
console.log(proto.name); // 触发此时get中的this可以读取proto中的name
// 输出结果
// 被赋值了!!!
// 被读取了!!!
// jack
下面是第二个例子:
function Person () {
};
const p1 = new Person("tom");
const p2 = new Person("tom");
Object.defineProperty(Person.prototype, "name", {
set: (val) => {
console.log("属性值被赋值了!!!");
this.name = val;
},
get: () => {
console.log("属性值被访问了!!!");
return this.name;
}
});
p1.name = "jack";
console.log(Person.prototype.name); // jack
console.log(p2.name) //jack
// 输出结果
// 属性值被访问了!!!
// jack
// 属性值被访问了!!!
// jack
本文只是前端初学者的个人理解...如有错误请轻喷...