持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第15天,点击查看活动详情
如果我们想要对一个属性进行比较精准的操作控制,那么我们就可以使用属性描述符
- 通过属性描述符可以精准的添加或修改对象的属性
- 属性描述符本质就是对属性进行配置的一个配置对象
- 属性描述符需要使用 Object.defineProperty 来对属性进行添加或者修改
Object.defineProperty()
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象
const user = {
name: 'Klaus',
age: 23
}
// 修改原本属性
// 参数一 - 需要修改的对象
// 参数二 - 需要修改的属性
// 参数三 - 属性描述符
// 返回值 - 修改后的对象
// 因为defineProperty 不是一个纯函数,会改变传入的对象,所以返回值很少使用
Object.defineProperty(user, 'name', {
value: 'Alex'
})
// 添加属性
const obj = Object.defineProperty(user, 'address', {
value: 'shanghai',
enumerable: true
})
console.log(user.name) // => Alex
console.log(user.address) // => shanghai
console.log(obj) // => { name: 'Alex', age: 23, address: 'shanghai' }
属性描述符
分类
属性描述符的类型有两种:
- 数据属性(Data Properties)描述符(Descriptor)
- 存取属性(Accessor访问器 Properties)描述符(Descriptor)
数据属性描述符
属性描述符 | 功能 |
---|---|
[[Configurable]] | 属性是否可以使用delete删除,属性的数据描述符是否可以被再次修改 |
[[Enumerable]] | 属性是否是可迭代的(即可以使用for-in循环,Object.keys等获取) |
[[Writable]] | 属性是否是只读的 |
[[value]] | 属性值,读取属性时会返回该值,修改属性时,会对其进行修改 |
对于属性描述符(除[[value]]外),默认值如下:
- 当我们直接在一个对象上定义某个属性时,这个属性的默认值为true
- 当我们通过属性描述符定义一个属性时,这个属性的默认值为false
对于属性描述符[[value]], 默认值为undefined
对于不可枚举的属性,如果直接打印一个对象的时候,依旧可以在打印的对象中看到对应的不可枚举属性,但是其颜色会比正常属性要淡,表明其是一个不可枚举属性,在遍历的时候不会被输出
存储属性描述符
属性描述符 | 功能 |
---|---|
[[Configurable]] | 和数据属性描述符中[[Configurable]]规则一致 |
[[Enumerable]] | 和数据属性描述符中[[Enumerable]]规则一致 |
[[get]] | 获取属性时会执行的函数。默认为undefined |
[[set]] | 设置属性时会执行的函数。默认为undefined |
Object.defineProperties
Object.defineProperties() 方法直接在一个对象上定义 多个
新的属性或修改现有属性,并且返回该对象
const user = {}
Object.defineProperties(user, {
name: {
value: 'Klaus',
enumerable: true
},
age: {
value: 23,
enumerable: true
}
})
console.log(user) // => { name: 'Klaus', age: 23 }
其它方法补充
静态方法
获取对象的属性描述符
const user = {
name: 'Klaus',
age: 23
}
// 获取对象单一属性的属性描述符
console.log(Object.getOwnPropertyDescriptor(user, 'name'))
// 获取对象所有属性的属性描述符
console.log(Object.getOwnPropertyDescriptors(user))
禁止对象添加新属性
const user = {
name: 'Klaus',
age: 23
}
Object.preventExtensions(user)
密封对象 - 禁止删除属性或对对象的属性进行配置
const user = {
name: 'Klaus',
age: 23
}
Object.seal(user)
冻结对象 - 不允许修改属性的值
const user = {
name: 'Klaus',
age: 23
}
Object.freeze(user)
原型的设置和获取
setPrototypeOf
- 用于设置原型对象
getPrototypeOf
- 用于获取原型对象
const obj = {
name: 'Klaus',
age: 23
}
const user = {}
// 设置user的原型对象为obj对象
Object.setPrototypeOf(user, obj)
// 获取user对象的原型对象
console.log(Object.getPrototypeOf(user) === obj) // => true
实例方法
hasOwnProperty
- 对象是否有某一个属于自己的属性(不是在原型上的属性)
const obj = {
name: 'Klaus',
age: 23
}
const user = Object.create(obj)
user.address = 'shanghai'
console.log(user.hasOwnProperty('name')) // => false
console.log(user.hasOwnProperty('address')) // => true
in/for in 操作符
- 判断某个属性是否在某个对象或者对象的原型上
const obj = {
name: 'Klaus',
age: 23
}
const user = Object.create(obj)
user.address = 'shanghai'
console.log('name' in user) // => true
console.log('address' in user) // => true
// for-in遍历并不会输出object上的一些方法,如hasOwnProperty, toString,valueOf等
// 因为这些属性和方法的enumerable和原型对象上的constructor的enumerable一样都是false
for (let key in user) {
console.log(key)
/*
=>
name
age
address
*/
}
instanceOf
- 用于检测构造函数的pototype,是否出现在某个实例对象的原型链上
function Person(name, age) {
this.name = name
this.age = age
}
function Student(name, age, sno) {
Person.call(this, name, age)
this.sno = sno
}
Object.setPrototypeOf(Student.prototype, Person.prototype)
const stu = new Student('Klaus', 23, 1810166)
// instanceof 主要用来检测 构造函数和实例对象之间的关系
console.log(stu instanceof Student) // => true
console.log(stu instanceof Person) // => true
isPrototypeOf
- 用于检测某个对象,是否出现在某个实例对象的原型链上
const obj = {
name: 'Klaus',
age: 23
}
const user = Object.create(obj)
// isPrototypeOf用于判断 某个对象 是否存在于参数对象的原型链上
// isPrototypeOf 主要用于判断 对象和对象之间的关系,这是其和instanceOf之间最大的区别
console.log(obj.isPrototypeOf(user)) // => true