属性描述符
Property Descriptor 属性描述符 用于描述一个属性的相关信息 属性有哪些信息:目前知道的就只有两个一个是属性的名字,一个是属性的值; 其实属性还有其他的一些相关信息,比如这个属性能否在for-in循环中被循环出来,这个就叫做可枚举性,具体还有那些可以 通过Object.getOwnPropertyDescriptor(对象,属性名)
可以得到一个对象的某个属性的属性描述符
const obj={
a:1,
b:2
}
const desc=Object.getOwnPropertyDescriptor(obj,'a')
console.log(desc)
由此可以看出属性描述符是一个对象
- value:属性值
- configurable:该属性的描述符是否可以修改
- enumerable:该属性是否可以被枚举
- writable: 该属性的值是否可以被重新赋值
Object.getOwnPropertyDescriptors(obj)
可以得到某个对象的所有属性描述符
const obj={
a:1,
b:2
}
const allDesc= Object.getOwnPropertyDescriptors(obj)
console.log('allDesc',allDesc)
如果需要为某个对象添加属性时 或修改属性时,配置其属性描述符,可以使用下面的代码,一般情况下都是在对象中改或者直接obj.属性名=修改值; 下面代码则用不一样的方法修改值或者属性修饰符
const obj={
a:1,
b:2
}
//这里把属性的描述符变为不能修改
Object.defineProperty(obj,'a',{
value:3,
configurable:false,
})
const desc=Object.getOwnPropertyDescriptor(obj,'a')
console.log('desc',desc)
这里再次修改值和还原 configurable的值看看能不能修改属性修饰符
const obj={
a:1,
b:2
}
//这里把属性的描述符变为不能修改
Object.defineProperty(obj,'a',{
value:3,
configurable:false,
})
const desc=Object.getOwnPropertyDescriptor(obj,'a')
console.log('desc',desc)
Object.defineProperty(obj,'a',{
value:4,
configurable:true,
})
const desc2=Object.getOwnPropertyDescriptor(obj,'a')
console.log('desc',desc2)
这就说明你把configurable的值改为false后你就不能再进行属性修饰的修改了,这里注意:值可以修改的
修改enumerable的值
const obj={
a:1,
b:2
}
Object.defineProperty(obj,'a',{
value:3,
configurable:false,
enumerable:false
})
const desc=Object.getOwnPropertyDescriptor(obj,'a')
console.log('desc',desc)
for (const prop in obj) {
console.log(prop)
}
是否可枚举可以看出把enumerable的值改为false,就不能遍历了,其中他还影响这个Object.keys()和Object.values()方法
const props=Object.keys(obj);
console.log(props);
const values=Object.values(obj);
console.log(values);
修改writable的值
const obj={
a:1,
b:2
}
Object.defineProperty(obj,'a',{
value:3,
configurable:false,
enumerable:false,
writable:false
})
const desc=Object.getOwnPropertyDescriptor(obj,'a')
console.log('desc',desc)
obj.a=10;
console.log(obj);
存取器属性
属性描述符总,如果配置了get和set中的任何一个,该属性,不再是一个普通属性,而变成了存取器属性
get和set配置均为函数,如果一个属性时存取器属性,则读取该属性时,会运行get方法,将get方法得到的返回值作为属性值;如果给该属性赋值,则会运行set
const obj={
a:1,
b:2
}
Object.defineProperty(obj,'a',{
get(){
console.log("运行了属性a的get函数")
},
set(val){
console.log('运行了set方法', val)
}
})
obj.a=10//运行了set方法
console.log(obj.a);//调用了get方法
为什么打印值为undefined?因为在调用obj.a的时候调用了get方法,但是get方法没有返回值所有就是undefined
哪下面运行结果打印的是什么
obj.a=obj.a+1相当于set(obj.a+1) set(get()+1)解析:先调用set方法,调用时发现参数里面有obj.a调用get方法 ,
调用get方法发现其返回值是undefined,undefined+1的值为NaN,虽然set函数计算的结果是NaN,但是最后调用get函数没有返回值结果还是undefined
obj.a=obj.a+1//相当于 set(obj.a+1) set(get()+1)
console.log(obj.a);//调用了get方法
想要把这个get和set做成一个正常的例子 有人就这样想,set函数中拿到obj.a然后把val的值赋给obj.a;然后再get函数中返回obj.a 如果这样做就会发现浏览器卡死进入无限递归,为什么会这样 当你再调用obj.a的时候就会运行get方法,然而get方法里面return的obj.a也相当于再调用get方法, 本来运行get方法需要算出值来,但是return obj.a相当于没等get算出来就又调用get方法所有就进行了无限递归;运行set方法也是一样的;所以使用存取器属性,千万不要再get里面去读取属性本身,再set里面也不要去设置属性本身
Object.defineProperty(obj,'a',{
get(){
console.log("运行了属性a的get函数");
return obj.a
},
set(val){
console.log('运行了set方法', val)
obj.a=val
}
})
console.log(obj.a);
那如何才能正确的拿到该属性呢
你可以给这个对象的别的属性赋值如obj._a=val,然后再get函数里面返回这个设置的属性 return obj._a这样就可以拿到和赋值了
Object.defineProperty(obj,'a',{
get(){
console.log("运行了属性a的get函数");
return obj._a
},
set(val){
console.log('运行了set方法', val)
obj._a=val
}
})
obj.a=10
console.log(obj.a);
存取器属性最大的意义,在于可以控制属性的读取和赋值
如下例子这样就可以控制年龄属性的值
let person={
name:'wq',
}
Object.defineProperty(person,'age',{
get(){
return person._age
},
set(val){
if(typeof val !=='number'){
throw new TypeError('年龄必须是一个数字')
}
if(val>200){
val=200;
} else if(val<0){
val=0
}
person._age=val
}
})
person.age=-10
console.log(person.age);
person.age=1000
console.log(person.age);
person.age='213qref'
console.log(person.age);