亲宝软件园·资讯

展开

js  Object.defineProperty

亦黑迷失 人气:0

Object.defineProperty()

Object.defineProperty() 用于给一个对象定义一个新属性或是修改某个现有属性,并返回此对象,事实上就算不定义变量去接收返回值,该对象也会被直接修改(所以它不是一个纯函数)。它接收 3 个参数,第 1 个是要定义属性的对象;第 2 个是要定义或修改的属性的属性名或 Symbol;第 3 个是对该属性的描述,称之为属性描述符,为一个对象,可以拥有 4 个 key。

属性描述符

属性描述符可以分为 2 类——数据属性描述符存取属性描述符,它们有 2 个共同的可拥有的 key: configurable 和 enumerable。区别在于剩下 2 个,数据属性描述符为 value 和 writable 而存储属性描述符为 get 和 set。

下图截取自 MDN 文档:

一个属性如果拥有了 value 或 writable,那么它就是数据描述符,它就不能同时拥有 get 或 set;反之,如果拥有了 get 或 set,那么它就是存取描述符,同理不能再拥有 value 或 writable。 接下来详细介绍下上图中的 6 个特性:

configurable

描述该属性是否可以被删除

'use strict'
const obj = { singer: 'Jay' }
Object.defineProperty(obj, 'singer', {
  configurable: false
})
delete obj.singer

我们将 obj 的 singer 属性的 configurable 设置为了 false,所以在第 6 行使用 delete 删除 singer 属性时,在严格模式下(在非严格模式下不会报错,但也不会删除 singer,即静默失败),浏览器会报错:

2. 描述该属性是否可以再次通过 Object.defineProperty() 来修改属性描述:

const obj = { singer: 'Jay' }
Object.defineProperty(obj, 'age', {
  configurable: false
})
Object.defineProperty(obj, 'age', {
  enumerable: flase
})

当我们将 age 的 configurable 设为 false 后,在第 6 行试图修改 age 的 enumerable 特性,会报错“TypeError: Cannot redefine property: age”。当设置 configurable 为 false 后(writable 默认为 false),该属性的任意描述符(enumerable,value 或是 get、set)都不能被改变了。但是,如果初始定义时 writable 为 true,即使 configurable 为 false,那么接下去还是可以将 writable 改为 false,同时也可以修改 value:

const obj = new Object()
obj.singer = 'Jay'
Object.defineProperty(obj, 'age', {
  configurable: false,
  writable: true
})
console.log(obj.age) // undefined
Object.defineProperty(obj, 'age', {
  writable: false,
  value: 40
})
console.log(obj.age) // 40

注:上面采用了 new Object() 的方式定义了一个对象,和直接通过字面量定义对象作用一样。

描述该属性是否可以修改属性描述符类型:

const obj = { singer: 'Jay' }
Object.defineProperty(obj, 'singer', {
  configurable: false
})
Object.defineProperty(obj, 'singer', {
  get() {
    return 'Zhou'
  }
})

上面这个例子里,在第 1 行我们通过字面量的方式直接定义了 obj 对象,其属性 singer 的描述符默认为数据描述符,在第 3 行我们将其的 configurable 设为 false,然后在第 6 行给它定义一个 getter 函数,也就是试图将它改为存取描述符,浏览器同样会报 "Cannot redefine property: singer" 错误。 如果我们新定义一个 age 属性,让其的属性描述符为存取描述符,但是 configurable 依旧设置为 false:

const obj = { singer: 'Jay', _age: 40 }
Object.defineProperty(obj, 'age', {
  configurable: false,
  enumerable: true,
  get() {
    return this._age
  },
  set(val) {
    this._age = val
  }
})
Object.defineProperty(obj, 'age', {
  value: 35
})

那么我们想再次给 age 的属性描述符一个 value 特性,想将之改为数据描述符,浏览器也会报错。

enumerable

描述该属性是否是可枚举的。比如现在有如下代码,我们通过 Object.defineProperty() 给 obj 定义 age 属性, 并设置 enumerable 为 false:

var obj = { singer: 'Jay' }
Object.defineProperty(obj, 'age', {
  enumerable: false
})

现在,当我使用 for in 遍历 obj 时,只能得到 singer 而得不到 age,因为 for in 是以任意顺序遍历 obj 的除 Symbol 以外的可枚举属性(包括原型上的属性):

for (const key in obj) {
  console.log(key) // singer
}

使用 Object.keys() 遍历得到的数组也只包含 singer,因为 Object.keys() 返回的是 obj 自身的可枚举属性组成的数组:

console.log(Object.keys(obj)) // ['singer']
复制代码

如果是直接打印 obj 对象,那么在 Node.js 中运行将看不到 age,在 Chrome 浏览器中可以看到,但是 age 是浅色的:

如果想查看一个 enumerable 为 false 的属性(比如 obj 的 age 属性),除了可以直接通过 obj.age 查看,还可以通过 Object.getOwnPropertyNames()——返回自身除 Symbol 值作为名称的属性之外的所有属性,或是 Reflect.ownKeys()——获取自身所有的属性:

console.log(Object.getOwnPropertyNames(obj)) // [ 'singer', 'age' ]
console.log(Reflect.ownKeys(obj)) // [ 'singer', 'age' ]

writable

数据描述符专有特性,描述属性是否可修改。

'use strict'
var obj = { singer: 'Jay' }
Object.defineProperty(obj, 'singer', {
  writable: false
})
obj.singer = 10

上例中,在严格模式下,在第 6 行给 writable 为 false 的属性 singer 赋值,会报错 “TypeError: Cannot assign to read only property 'age' of object '#'”。在非严格模式下不会报错,但也不会修改属性 singer 的值。 另外,通过前面的例子可以看出,writable 的优先级是高于 configurable 的。

value

数据描述符专有特性,为属性的值。读取属性时返回该值;修改属性时则修改该值。

get

当属性被获取时,会执行 getter 函数。

set

当属性被设置时,会执行 setter 函数。

const obj = { singer: 'Jay', _age: 40 }
Object.defineProperty(obj, 'age', {
  get() {
    return this._age
  },
  set(value) {
    this._age = value
  }
})
console.log(obj.age) // 40
obj.age = 50
console.log(obj.age) // 50

用存取描述符定义的属性,直接打印查看对象时,比如我们将上例中 obj 的 age 属性的 enumerable 设为 true,然后 console.log(obj),会发现其结果为“{ singer: 'Jay', _age: 40, age: [Getter/Setter] }” 。 顺便说一句,vue2 的响应式原理就用到了 getter 和 setter,具体可前往《vue.js数据响应式原理解析》。

默认值

直接给对象定义属性时:

通过属性描述符定义一个属性时:

获取属性的描述符

如果想要验证上面的结论,我们可以通过 Object.getOwnPropertyDescriptor(对象, '属性名') 查看某个对象自有属性的属性描述符,或是 Object.getOwnPropertyDescriptors(对象) 查看某个对象的所有的自身属性的属性描述符。

Object.defineProperties()

Object.defineProperty() 是一次定义 / 修改一个属性,传入 3 个参数。如果我们想一次性通过属性描述符定义 / 修改多个属性,可以使用 Object.defineProperties(),它传入 2 个参数,第 1 个参数为要定义属性的对象;第二个参数为一个对象,其键名为要定义的属性,键值为一个对象,里面就是关于该属性的属性描述符定义。

比如:

const obj = { singer: 'Jay', _age: 40 }
Object.defineProperties(obj, {
  age: {
    configurable: true,
    enumerable: true,
    get() {
      return this._age
    },
    set(val) {
      this._age = val
    }
  },
  gender: {
    configurable: true,
    enumerable: true,
    writable: true,
    value: '男'
  }
})

对象本身的两个方法

其实每个对象本身都可以直接使用 getter —— 得到当前属性值的回调函数,和 setter —— 监视当前属性值变化的回调函数,来定义属性,比如:

const obj = {
  firstName: 'Jay',
  lastName: 'Zhou',
  get fullName() {
    return this.firstName + ' ' + this.lastName
  },
  set fullName(val) {
    const tempArr = val.split(' ')
    this.firstName = tempArr[0]
    this.lastName = tempArr[1]
  }
}

加载全部内容

相关教程
猜你喜欢
用户评论