Object.defineProperty 方法学习

208 阅读3分钟

之前有了解到 Object.defineProperty 方法,能够在对象获取值或者设置值的时候进行一些操作,且 Vue.js 的双向绑定,也是通过这个方法实现的,所以现在觉得有必要深入的了解一番。

首先,先看看 Object.defineProperty 的基础用法,这个方法接收三个参数:

  1. 属性所在对象
  2. 属性的名字
  3. 描述符对象(descriptor)

其中,描述符(descriptor)对象的属性必须是:

  1. configurable: 表示能否通过 delete 删除属性从而重新定义,能否修改属性的特性,或者能否把属性修改为访问器属性,默认值为 false
  2. enumerable: 表示能否通过 for-in 循环返回属性,默认值为 false
  3. writable: 表示能够修改属性的值,默认值同为 false
  4. value: 这个属性的数据值,就是我们常说的 value 值,当我们读取属性值的时候,就是从这个位置读,当我们给属性赋值时,也是从这个位置写入,不同于上面的属性,这个属性的默认值是 undefined
  5. get: 在读取属性时调用的函数,默认值为 undefine
  6. set:在写入属性时调用的函数,默认值为 undefine

举个例子

var language = {}
Object.defineProperty(language, 'name', {
  writable: false,
  value: 'JavaScript'
})
console.log(language.name); // JavaScript
language.name = 'PHP';
cosnole.log(language.name); // JavaScript

以上代码中创建了一个 language ,并定义了属性为 name ,值为 JavaScript 的只读对象。这个属性的值是不可修改的,如果对这个属性进行重新赋值,在非严格模式下,赋值操作会被忽略;在严格模式下,赋值操作会导致程序抛出错误。

类似的操作也不适用 configurable 属性,例如

var language = {}
Object.defineProperty(language, 'name', {
  configurable: false,
  value: 'JavaScript'
})
console.log(language.name); // JavaScript
delete language.name;
cosnole.log(language.name); // JavaScript

以上代码中创建了一个 language ,并定义了属性为 name ,值为 JavaScript 的不可配置对象。这个属性是不可配置的,如果对这个属性进行删除处理,在非严格模式下,删除操作会被忽略;在严格模式下,删除操作会导致程序抛出错误。

不同于 writable ,一旦设置属性的 configurable 的值为 false , 就不能再更改为 true 了,如果尝试这样操作也会导致程序报错。

在调用 Object.defineProperty 方法时,如果不指定, configurableenumerablewritable 特性的默认值都是 false

这里面要重点说一下 getset 属性,看下面的例子

var language = {
  _name: 'JavaScript',
  author: 'Brendan Eich',
  birth: 1995
}

Object.defineProperty(language, "name", {
  get: function () {
    return this._name;
  },
  set: function ( newValue ) {
    if ( newValue !== this._name) {
      this._name = 'this is not ' + this._name;
      this.author = null;
      this.birth = 0000;
    }
  }
})

console.log(language.name); // JavaScript
language.name = 'PHP';
console.log(language.name); // this is not JavaScript

从以上代码可以看出,通过 Object.defineProperty 给对象 language 创建了新属性 name ,而 name 设置了 getset 函数。 get 函数返回 _nameJavaScriptset 函数通过判断传入的新值 newValue 是否等于 _name 值,如果不等于则会给 _nameauthorbirth 重新赋值。

由于为对象定义多个属性的可能性很大, ECMAScript5 又定义了一个 Object.defineProperties 方法,利用这个方法可以通过描述符一次定义多个属性,以下为使用例子

var language = {}
Object.defineProperties(language, {
  _name: {
    writable: true,
    value: 'JavaScript'
  },
  year: {
    writable: true,
    value: 1995
  },
  author: {
    writable: true,
    value: 'Brendan Eich'
  },
  name: {
    get: function () {
      return this._name;
    },
    set: function ( newValue ) {
      if ( newValue !== this._name) {
        this._name = 'this is not ' + this._name;
        this.author = null;
        this.birth = 0000;
      }
    }
  }
})

这里需要说明的是,在定义属性时,如果这个值是需要改变的,需要加上 writable: true 才行,要不属性的的默认 writable 是为 false