1. 定义及参数
MDN
对该Object.defineProperties()
方法的定义:
Object.defineProperty()
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
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);
}