JS高级 - 对象属性补充

133 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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' }

属性描述符

分类

属性描述符的类型有两种:

  1. 数据属性(Data Properties)描述符(Descriptor)
  2. 存取属性(Accessor访问器 Properties)描述符(Descriptor)

image.png

数据属性描述符

属性描述符功能
[[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