1. 定义及参数
MDN 对该Object.defineProperties()方法的定义:
Object.defineProperty(obj, prop, descriptor)
- 可接收三个参数:
obj:要定义属性的对象。prop:要定义或修改的属性的名称或 Symbol 。descriptor:要定义或修改的属性描述符。
2. 属性描述符分类
属性描述符主要有两种形式:数据描述符和存取描述符;
- 数据描述符特有的两个属性:
value和writable; - 存取描述符特有的两个属性:
get和set;
两种形式的属性描述符不能混合使用,否则会报错,下面是一个错误的示范:
var obj = {};
var initName = ''
//TypeError: Invalid property descriptor.
//Cannot both specify accessors and a value or writable attribute, #<Object>
Object.defineProperty(obj, "name", {
value: 'new',
writable: true,
get: function(){
console.log('get name')
return initName
},
set: function(val){
console.log('set name')
initName = val
}
});
我们简单想一下就能理解为什么两种描述不能混合使用;value用来定义属性的值,而get和set同样也是定义和修改属性的值,两种描述符在功能上有明显的相似性。
虽然数据描述符和存取描述符不能混着用,但是他们均能分别和configrable、enumerable一起搭配使用,下面表格表示了两种描述符可以同时拥有的健值:

3. Object.defineProperty() 使用
这是后续要操作的对象:
// 对象
const obj = {
name: 'zhLeon521',
age: 18
}
3.1 value(设置属性值)
给对象添加新的属性 height:
Object.defineProperty(obj, 'height', {
value: 666
})
console.log(obj) //{ name: 'zhLeon521', age: 18 }
这里描述符中的 value 值即是需要在对象上定义或者修改的属性值(如果对象上本身有该属性,则会进行修改操作);除了字符串,还可以是JavaScript的其他数据类型(数值,函数等)。
属性描述符是个对象,那么就有很多操作的地方了,它除了 value这个属性,还有以下:

3.2 writable(控制属性值是否可以修改)
当 Object.defineProperty() 中没有设置 writable 时,如果要去修改这里面的属性值,是没法被修改的(默认值为 false)。

给 Object.defineProperty() 中设置 writable: true 时,可以修改该属性值了。

2.3 enumerable(控制属性是否可枚举)
当 Object.defineProperty() 中没有设置 enumerable 时,如果要去遍历整个对象的键值,是没法遍历到通过 Object.defineProperty() 设置的属性值的。

给 Object.defineProperty() 中设置 enumerable: true 时,可以遍历到该属性值了。

2.4 configurable(控制对象是否可以重新配置操作事项)
我又有了新需求:上面我已经让对象可枚举了,然后我又想要通过一个新的 Object.defineProperty() 让属性值不可枚举。
如果什么都不设置的话,控制台是会报错的:

给 Object.defineProperty() 中设置 configurable: true 时,可以重新配置事项了。

2.5 get
get方法是用来获取属性值。
get 方法中通过 return 返回设置的属性值,在 return 之前还可以进行一些逻辑操作:

2.6 set
set方法是用来更新属性值。
set方法接收一个参数,这个参数就是设置的新的属性值,然后在方法中将这个新的值设置给自定义的属性。- 一个错误的例子:
Object.defineProperty(obj, 'height', {
// 设置新的属性值
set(newValue) {
console.log('任意设置时需要的自定义操作');
this.height = newValue;
}
});
obj.height = '111'

-
这个时候,控制台会报错,这是因为
set方法本来就是监听this.height(属性值) 的更新的的方法,当使用this.height = newValue这种方式给属性值赋新的值的话,就形成了一个递归,这个值更新了又更新。 -
正确的操作应该是在外面设置一个变量,将更新的值设置给这个变量,再用 get 方法来监听获取该变量的值。
let heightValue = '178';
Object.defineProperty(obj, 'height', {
// 获取属性值
get() {
console.log('任意获取时需要的自定义操作');
return heightValue;
},
// 设置新的属性值
set(newValue) {
console.log('任意设置时需要的自定义操作');
heightValue = newValue;
}
});
obj.height = '111'
console.log(obj.height);

4. 重点
对于 Object.defineProperty 来说,只可以对对象中的某一个属性进行监听,如果要监听整个对象的话,需要进行遍历。
const obj = {
name: 'zhLeon521',
age: 18,
height: 178
}
for (const k in obj) {
let value = obj[k];
Object.defineProperty(obj, k, {
// 获取属性值
get() {
console.log('任意获取时需要的自定义操作');
return value;
},
// 设置新的属性值
set(newValue) {
console.log('任意设置时需要的自定义操作');
value = newValue;
}
});
console.log(value);
}











