JavaScript:访问器属性和getter/setter

3,574 阅读2分钟

对象和属性

JS中,对象是键值对的集合。这些值是对象的属性,它可以是一个基本类型或对象,也可以是方法(method)。

比如:

const obj = {
  name: 'Brynn',
  sayName() {
    console.log('Brynn!')
  },
}
console.log(obj)
console.log(obj.sayName.name) // sayName

上面这个对象有一个name基本类型,和一个greet方法。

字面量定义属性

用上面这种定义方法是最简单的,称为用字面量定义。 将对象的属性和方法直接写在对象内。

const obj = {
  name: 'Brynn',
  sayName() {
    console.log('Brynn!')
  },
  greet: function () {
    console.log('Hello!')
  },
}
console.log(obj.sayName.name) // sayName
console.log(obj.greet.name) // greet

其中,定义方法时,有两种写法。这两种写法的函数都是有名字的。

defineProperty配置属性

除此之外,还可以用Object.defineProperty()配置一个属性,用Object.getOwnPropertyDescripter()查询配置具体情况。

const obj = {}
Object.defineProperty(obj, 'name', {
  value: 'Brynn',
  writable: true,
  enumerable: true,
  configurable: true,
})
Object.defineProperty(obj, 'sayName', {
  value: function sayName() {
    console.log('Brynn!')
  },
  writable: true,
  enumerable: true,
  configurable: true,
})
console.log(obj)
console.log(obj.sayName.name) // sayName

propertyDescriptor

Object.defineProperty中出现了一些属性(property)的特性(attribute),这些特性如下:

  • value: 属性的值
  • writable: 是否可修改属性的value
  • set: 写入属性时调用的函数(使用Brynn.name = 'Boolean'就会调用set函数)
  • get:获取属性时调用的函数(使用console.log(Brynn.name)就会调用get函数)
  • configurable:是否可修改属性的attribute,以及只有可配置属性的才能被delete删除
  • enumerable: 是否可枚举

可枚举的属性才能通过Object.keys(target)以及for in/of获取,可枚举和不可枚举的都可以通过Object.getOwnPropertyNames(target)获取)

这些特性有默认值,

  1. 通过字面量定义的:writable、configurable、enumerable都是true
  2. 通过defineProperty配置的writable、configurable、enumerable都是false
const obj = { name: 'Brynn' }

Object.defineProperty(obj, 'sayName', {
  value: function sayName() {
    console.log('Brynn!')
  },
})
let descriptors = Object.getOwnPropertyDescriptors(obj)
console.log(descriptors) // {name:{value: "Brynn", writable: true, enumerable: true, configurable: true}, sayName:  {writable: false, enumerable: false, configurable: false, value: ƒ}}

这些特性不能同时全部存在于一个属性上,根据这一点,属性又分为数据属性访问器属性

  • 数据属性:有value/writable/enumerable/configurable四个特性
  • 访问器属性: 有set/get/enumerable/configurable四个特性

访问器属性和getter/setter(存值函数/取值函数)

访问器属性是对象属性的两个特性,setter/getter是在定义对象方法

使用访问器属性

const obj = {}
Object.defineProperty(obj, 'foo', {
  get() {
    return 'brynn'
  },
  set(x) {
    this.foo = x
  },
  enumerable: true,
  configurable: true,
})

let descriptors = Object.getOwnPropertyDescriptors(obj)
console.log(descriptors)
console.log(obj)

使用setter/getter

const obj = {
  get foo() {
    return 'brynn'
  },
  set foo(x) {
    this.foo = x
  },
}
let descriptors = Object.getOwnPropertyDescriptors(obj)
console.log(descriptors)
console.log(obj)

区别是

  1. 用defineProperty添加属性更灵活,可以动态添加
  2. 特性的默认值不同,defineProperty的默认值是false,setter/getter的默认值是true

作为原型

这两种方法构造出的对象作为另一个对象的原型,都会使得另一个对象在控制台输出时有对应的属性,但它并不是真正的属性(getOwnDescriptors没有)

const obj = {}
Object.defineProperty(obj, 'foo', {
  get() {
    return 'brynn'
  },
  set(x) {
    this.foo = x
  },
  enumerable: true,
  configurable: true,
})

let upObj = {
  value: 'test',
}
upObj.__proto__ = obj
let descriptors = Object.getOwnPropertyDescriptors(upObj)
console.log(upObj)
console.log(descriptors)