一、Object.defineProperty

属性都是直接定义在对象内部,或者直接添加到对象内部的

但是这样就不能对这个属性进行一些限制;比如属性是否可以通过delete删除,是否能在for in遍历的时候被遍历出来

如果想要对一个属性进行比较精准的操作控制,就可以使用属性描述符

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

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

1
Object.defineProperty(obj, prop, descriptor)

可接受三个参数:

  • obj是要定义属性的对象
  • prop是要定义或修改的属性名称或Symbol
  • descriptor是要定义或修改的属性描述符

返回值

  • 被传递给函数的对象

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

  • 数据属性描述符
  • 存取属性描述符

二、数据属性描述符

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

  • [[Configuable]]: 表示属性是否可以通过delete删除属性,是否可以修改它的特性,或者是否可以将它修改为存取属性描述符

    • 当直接在对象上定义某个属性时,这个属性的 [[Configuable]]true
    • 当通过属性描述符定义一个属性时,这个属性的 [[Configuable]]false
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    
    var obj = {
      name: 'mocha',
      age: 18
    }
    
    Object.defineProperty(obj, 'name', {
      configurable: false // 告诉js引擎,obj对象的name属性不可以被删除
    })
    
    delete obj.name
    console.log(obj)  // { name: 'mocha', age: 18 }
    
    // 通过 Object.defineProperty 添加一个新的属性
    Object.defineProperty(obj, 'address', {})
    delete obj.address
    console.log(obj)  // { name: 'mocha', age: 18 }
    
    
  • [[Enumerable]]: 表示属性是否可以通过for-in或者Object.keys()返回该属性

    • 当直接在对象上定义某个属性时,这个属性的 [[Enumerable]]true
    • 当通过属性描述符定义一个属性时,这个属性的 [[Enumerable]]false
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    var obj = {
      name: 'mocha',
      age: 18
    }
    
    Object.defineProperty(obj, 'name', {
      enumerable: false
    })
    
    const keys = Object.keys(obj)
    console.log(keys) // age
    
  • [[Writable]]: 表示是否可以修改属性的值

    • 当直接在对象上定义某个属性时,这个属性的**[[Writable]]** 为 true
    • 当直接通过属性描述符定义属性时,这个属性的**[[Writable]]** 为false
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    var obj = {
      name: 'mocha',
      age: 18
    }
    
    Object.defineProperty(obj, 'name', {
      writable: false
    })
    
    obj.name = 'latte'
    console.log(obj.name)  // mocha
    
  • [[value]]: 属性的value值,读取属性时会返回该值,修改属性时,会对其进行修改

    • 默认情况下这个值是undefined
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    var obj = {}
    
    Object.defineProperty(obj, 'name', {
      configurable: false,
      enumerable: false,
      writable: false,
      value: 'mocha'
    })
    
    console.log(obj.name)  // mocha
    
    

三、存取属性描述符

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

  • [[Configuable]]: 表示属性是否可以通过delete删除属性,是否可以修改它的特性,或者是否可以将它修改为存取属性
    • 和数据属性描述符一致
    • 当直接在对象上定义某个属性时,这个属性的 [[Configuable]]true
    • 当通过属性描述符定义一个属性时,这个属性的 [[Configuable]]false
  • [[Enumerable]]: 表示属性是否可以通过for-in或者Object.keys()返回该属性
    • 和数据属性描述符一致
    • 当直接在对象上定义某个属性时,这个属性的 [[Enumerable]]true
    • 当通过属性描述符定义一个属性时,这个属性的 [[Enumerable]]false
  • [[get]]: 获取属性时会执行的函数,默认为undefined
  • [[set]]: 设置属性时会执行的函数,默认为undefined
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
var obj = {}

var _name = ''

Object.defineProperty(obj, 'name', {
  configurable: true,
  enumerable: false,
  set: function (value) {
	console.log('set方法被调用了', value)
	_name = value
  },
  get: function () {
	console.log('get方法被调用了')
	  return _name
  }
})

obj.name = 'mocha'  // 赋值后set函数被执行:set方法被调用了 mocha
obj.name = 'latte'  // 赋值后set函数被执行:set方法被调用了 latte

console.log(obj.name) // get方法被调用了  latte

四、Object.defineProperties

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
var obj = {
  _age: 18
}

Object.defineProperties(obj, {
  name: {
    writable: true,
    value: "mocha"
  },
  age: {
	get: function() {
	  return this._age
    }
  }
})

五、对象的其他方法补充

  • 获取对象的属性描述符

    • getOwnPropertyDescriptor

      1
      2
      3
      4
      5
      6
      
      var obj = {
        name: 'mocha',
        age: 18,
      }
      
      console.log(Object.getOwnPropertyDescriptor(obj, 'name'))
      

      image-20230703205325591

    • getOwnPropertyDescriptors

      1
      2
      3
      4
      5
      6
      
      var obj = {
        name: 'mocha',
        age: 18,
      }
      
      console.log(Object.getOwnPropertyDescriptors(obj))
      

      image-20230703205358591

  • 禁止对象扩展新属性:preventExtensions

    • 给对象添加新的属性会失败(在严格模式下会报错)

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      
      var obj = {
        name: 'mocha',
        age: 18,
      }
      
      Object.preventExtensions(obj)
      
      obj.address = '广州市'
      
      console.log(obj)  // { name: 'mocha', age: 18 }
      
  • 密封对象,不允许配置和删除属性:seal

    • 实际是调用preventExtensions

    • 并且将现有属性的configurable设置为false

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      
      var obj = {
        name: 'mocha',
        age: 18,
      }
      
      Object.seal(obj)
      
      delete obj.name
      
      console.log(obj)  // { name: 'mocha', age: 18 }
      
  • 冻结对象,不允许修改现有属性:freeze

    • 实际上是调用seal

    • 并且将现有属性的writable设置为false

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      
      var obj = {
        name: 'mocha',
        age: 18,
      }
      
      Object.freeze(obj)
      
      obj.name = 'latte'
      
      console.log(obj)  // { name: 'mocha', age: 18 }