Property Descriptor 属性描述符,是一个普通对象,用于描述一个属性的相关信息 如图下
其代码为
const obj = {
a:1,
b:"a"
}
查看属性描述符
通过Object.getOwnPropertyDescriptor(对象, 属性名)可以得到一个对象的某个属性的属性描述符
- value:属性值
- configurable:该属性的描述符是否可以修改 ,设置为false后无法再次修改属性描述符,但为该属性重新赋值有效
- enumerable:该属性是否可以被枚举
- writable:该属性是否可以被重新赋值
修改属性描述符
如果需要为某个对象添加属性时 或 修改属性时, 配置其属性描述符,可以使用下面的代码:
Object.defineProperty(对象, 属性名, 描述符);
Object.defineProperties(对象, 多个属性的描述符)
//这两个的区别为 第一个只能修改或添加一个属性的描述符,第二个可以修改或添加多个
示例
const obj = {
a:1,
b:"a"
}
Object.defineProperties(obj,{
a:{
configurable:false,
},
b:{
configurable:false,
}
})
// 上面修改属性a的属性描述符等同于下面的代码
Object.defineProperty(obj, "a" ,{
configurable:false,
})
重点 存取器属性
何为存取器属性
<div id="box"></div>
<script>
const box = document.getElementById('box')
console.dir(box)
</script>
如左图中这些属性的括号里都有三个点,而正常的属性(右图)都是有值的,即使没有赋值也会有null值,左图中带(…)这个都是存取器属性,左图中我挑选一些常用的元素上的存取器属性
存取器的属性值不会存储在内存中,正常属性的属性值都是存储在内存空间中的
故设置为存取器属性时,不能与**value与writable**共同存在
怎样设置存取器属性
属性描述符中,如果配置了 get 和 set 中的任何一个,则该属性,不再是一个普通属性,而变成了存取器属性。
get 和 set配置均为函数,如果一个属性是存取器属性,则读取该属性时,会运行get方法,将get方法得到的返回值作为属性值;如果给该属性赋值,则会运行set方法。
存取器属性最大的意义,在于可以控制属性的读取和赋值。
const obj = {
a: 1,
b: "a"
}
Object.defineProperties(obj, {
a: {
get() {
return obj._a;
},
set(val){
obj._a = val;
/**
* “_”为保留符号,切记此处不能使用 obj.a=val;因为现在属性a为存取器属性
* 存取器属性的读取与赋值都会调用其对应得函数,此处为赋值,假设此处写的是obj.a=val;那吗不就再次
* 调用自己的赋值函数吗(存取器属性的赋值为set()) 这样不就是无限递归吗,会导致栈溢出
* */
}
注意: “_”为保留符号,切记此处不能使用 obj.a=val;因为现在属性a为存取器属性,存取器属性的读取与赋值都会调用其对应得函数,此处为赋值,会运行set方法,假设此处写的是obj.a=val; 这句代码的意思不就是为属性a赋值吗,但是a现在为存取器属性,他会调用set方法,这样不就重新调用set方法,依次往后,无限调用set方法,不就无限递归啦,最后栈溢出。
当get与set方法中啥都不写的话,返回值为unfinished,你重新复制与读取都是unfinished,函数的默认返回值为unfinished
案例1 控制年龄的最小值与最大值
<p>
<span>姓名:</span>
<span id="name"></span>
</p>
<p>
<span>年龄:</span>
<span id="age"></span>
</p>
<script>
const spanName = document.getElementById("name")
const spanAge = document.getElementById("age")
const user = {}
Object.defineProperties(user, {
name: {
get() {
return spanName.innerText;
},
set(val) {
spanName.innerText = val;
}
},
age: {
get() {
return +spanAge.innerText;
},
set(val) {
if (typeof val !== "number") {
throw new TypeError("年龄必须是一个数字")
}
if (val < 0) {
val = 0;
} else if (val > 200) {
val = 200;
}
spanAge.innerText = val;
}
}
})