Skip to content

一. 属性描述符


1. 对属性操作的控制

  • 在前面我们的属性都是直接定义在对象内部,或者直接添加到对象内部的:

    js
    var obj = {
      name: 'later',
      age: '23'
    }
  • 但是这样来做的时候我们就不能对这个属性进行一些限制:

    • 比如这个属性是否是可以通过delete删除的?
    • 这个属性是否在for-in遍历的时候被遍历出来呢?
  • 如果我们想要对一个属性进行比较精准的操作控制,那么我们就可以使用属性描述符

    • 通过属性描述符可以精准的添加 或 修改对象的属性
    • 属性描述符需要使用Object.defineProperty()来对属性进行添加或者修改

2. Object.defineProperty()

  • Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象

    js
    Object.defineProperty(obj, prop, descriptor)
  • 可接受三个参数:

    • obj:要定义或修改属性的对象
    • prop:要定义或修改属性的名称 或 Symbol
    • descriptor:要定义或修改的属性描述符
  • 返回值:被传递给函数的对象

3. 属性描述符分类

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

    • 数据属性描述符(Data Properties Descriptor
    • 存取属性描述符(Accessor访问器 Properties Descriptor
    数据描述符存取描述符
    configurable
    enumerable
    value不可
    writable不可
    get不可
    set不可

二. 数据属性描述符


  • 数据属性描述符有如下四个特性:

    js
    Object.defineProperty(obj, "name", {
      configurable: false, // 告诉js引擎, obj对象的name属性不可以被删除
      enumerable: false, // 告诉js引擎, obj对象的name属性不可枚举(for in / Object.keys)
      writable: false, // 告诉js引擎, obj对象的name属性不写入(只读属性 readonly)
      value: "later" // 告诉js引擎, 返回这个value
    })
    
    // 测试configurable
    delete obj.name // 不可删除,产生静默失败
    obj.name = 'haha' // 不可修改
    console.log(Object.keys(obj)) // 返回obj对象的可枚举属性数组中不包含name属性
    console.log(obj.name) // 'later'
  • [[Configurable]]:表示属性是否可以通过delete删除属性,是否可以修改它的特性,或者是否可以将它修改为存取属性描述符

    • 当我们直接在一个对象上定义某个属性时,这个属性的[[Configurable]]默认为true
    • 当我们通过属性描述符定义一个属性时,这个属性的[[Configurable]]默认为false
    • 如果为false,不能通过再次defineProperty来对属性进行修改
  • [[Enumerable]]:表示属性是否可以通过for-inObject.keys()返回该属性

    • 当我们直接在一个对象上定义某个属性时,这个属性的[[Enumerable]]默认为true
    • 当我们通过属性描述符定义一个属性时,这个属性的[[Enumerable]]默认为false
  • [[Writable]]:表示是否可以修改属性的值

    • 当我们直接在一个对象上定义某个属性时,这个属性的[[Writable]]默认为true
    • 当我们通过属性描述符定义一个属性时,这个属性的[[Writable]]默认为false
  • [[value]]:属性的value值,读取属性时会返回该值,修改属性时,会对其进行修改

    • 默认情况下这个值是undefined

三. 存取属性描述符


  • 存取属性描述符有如下四个特性:

    js
    var obj = {
      name: 'later',
      age: 23
    }
    
    var _address = '深圳市'
    Object.defineProperty(obj, 'address', {
      enumerable: true,
      get: function() {
        return _address
      },
      set: function(value) {
        _ddress = value
      }
    })
    
    console.log(obj.address) // 深圳市
    obj.address = '邵阳市'
    console.log(obj.address) // 邵阳市
    • [[Configurable]]:表示属性是否可以通过delete删除属性,是否可以修改它的特性,或者是否可以将它修改为存取属性描述符
      • 和数据属性描述符是一致的
      • 当我们直接在一个对象上定义某个属性时,这个属性的[[Configurable]]true
      • 当我们通过属性描述符定义一个属性时,这个属性的[[Configurable]]默认为false
    • [[Enumerable]]:表示属性是否可以通过for-in或者Object.keys()返回该属性
      • 和数据属性描述符是一致的
      • 当我们直接在一个对象上定义某个属性时,这个属性的[[Enumerable]]true
      • 当我们通过属性描述符定义一个属性时,这个属性的[[Enumerable]]默认为false
    • [[get]]:获取属性时会执行的函数。默认为undefined
    • [[set]]:设置属性时会执行的函数。默认为undefined

    注意:

    • 一个描述符中,getset不能同时搭配valuewritable数据属性描述符使用,否则会产生一个异常

四. Object.definedProperties()


  • Object.defineProperties() 方法直接在一个对象上同时定义多个新的属性或修改现有属性,并且返回该对象

    js
    var obj = {
      _age: 18
    }
    
    Object.defineProperties(obj, {
      name: {
        writable: true,
        value: 'later',
      },
      age: {
        get: function() {
          return this._age
        },
        // value: 23
      }
    })
    
    console.log(obj.age) // 18
    console.log(Object.keys(obj)) // ['_age']
    console.log(obj) // {_age: 18, name: 'later'}

五. 对象的其他方法补充


  • 获取对象的属性描述符
    • getOwnPropertyDescriptor()(单个属性)
    • getOwnPropertyDescriptors()(所有的属性)
  • 禁止对象扩展新属性preventExtensions
    • 给一个对象添加新的属性会失败(在严格模式下会报错)
  • 密封对象,不允许配置和删除属性seal
    • 实际是调用preventExtensions
    • 并且将现有属性的configurable: false
  • 冻结对象,不允许修改现有属性freeze
    • 实际上是调用seal
    • 并且将现有属性的writable: false

Released under the MIT License.