智一面

 

Object.defineProperty()

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

注意: 应当直接在 Object 构造器对象上调用此方法,而不是在任意一个 Object 类型的实例上调用。

let obj = {}
// 直接在 Object 构造器对象上调用此方法,这里定义了一个新属性 name,并赋值 xbd。
Object.defineProperty(obj, 'name', {
  value: 'xbd'
})
console.log(obj.name) // 输出 xbd

// 以下在 Object 类型的实例上调用是错误的!!!实例并没有 defineProperty() 这个方法
obj.defineProperty(obj, 'name', {
  value: 'xbd'
})
语法
Object.defineProperty(obj, prop, descriptor)
参数
  • obj:要定义属性的对象。

  • prop:要定义或修改的属性的名称或 Symbol

  • descriptor:要定义或修改的属性描述符。

    该方法允许精确地添加或修改对象的属性。对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。数据描述符是一个具有值的属性,该值可以是可写的,也可以是不可写的。存取描述符是由 getter 函数和 setter 函数所描述的属性。

    • 这两种描述符都是对象。它们共享以下可选键值:

      • configurable

        当且仅当该属性的 configurable 键值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false。

        let obj = {}
        Object.defineProperty(obj, 'name', {
          configurable: false, // 默认 false
          value: 'xbd'
        })
        console.log(obj.name) // 输出 xbd
        delete obj.name // configurable 为 false 时不可删
        console.log(obj) // 输出 {name: 'xbd'}
        
        Object.defineProperty(obj, 'name', {
          configurable: true,
          value: 'xbd'
        })
        console.log(obj.name) // 输出 xbd
        delete obj.name // configurable 为 true 时可删
        console.log(obj) // 输出 {}
      • enumerable

        当且仅当该属性的 enumerable 键值为 true 时,该属性才会出现在对象的枚举属性中。默认为 false。

        let obj = {}
        Object.defineProperty(obj, 'name', {
          value: 'xbd',
          enumerable: true // 设置可被枚举
        })
        Object.defineProperty(obj, 'age', {
          value: 18,
          enumerable: false // 设置不可被枚举
        })
        Object.defineProperty(obj, 'sex', {
          value: 'man' // 默认不可被枚举
        })
        obj.height = '65kg' // 直接赋值的属性,enumerable 为 true
        
        for (let key in obj) {
          console.log(key) // 输出可枚举的 name 和 height,而 age 和 sex 为不可枚举属性,故无法遍历出
        }
        
        console.log(Object.keys(obj)) // ['name', 'height']
        
    • 数据描述符还具有以下可选键值:

      • value

        该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。

        let obj = {}
        Object.defineProperty(obj, 'name', {
          value: 'xbd'
        })
        console.log(obj.name) // 输出 xbd
      • writable

        当且仅当该属性的 writable 键值为 true 时,属性的值,也就是上面的 value,才能被赋值运算符改变。默认为 false。

        let obj = {}
        Object.defineProperty(obj, 'name', {
          writable: true,
          value: 'xbd'
        })
        console.log(obj.name) // 输出 xbd
        obj.name = 'xbd2' // writable 为 true 时可修改属性值
        console.log(obj.name) // 输出 'xbd2'
        
    • 存取描述符还具有以下可选键值:

      • get

        属性的 getter 函数,如果没有 getter,则为 undefined。当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的this并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值。默认为 undefined。

        let obj = {}
        let name = 'xbd'
         Object.defineProperty(obj, 'name', {
           get() {
             return name
           }
         })
         console.log(obj.name) // 输出 xbd
      • set

        属性的 setter 函数,如果没有 setter,则为 undefined。当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。默认为 undefined。

        let obj = {}
        let name = 'xbd'
        Object.defineProperty(obj, 'name', {
          get() {
            return name
          },
          set(newValue) {
            name = newValue
          }
        })
        console.log(obj.name) // 输出 xbd
        obj.name = 'xbd2' // 设置新值
        
    configurable enumerable value writable get set
    数据描述符 可以 可以 可以 可以 不可以
    存取描述符 可以 可以 不可以 不可以 可以

    如果一个描述符不具有 value、writable、get 和 set 中的任意一个键,那么它将被认为是一个数据描述符。一个描述符只能是数据描述符和存取描述符两者其中之一,不能同时是两者,所以如果一个描述符同时拥有 value 或 writable 和 get 或 set 键,则会产生一个异常。

    记住,这些选项不一定是自身属性,也要考虑继承来的属性。为了确认保留这些默认值,在设置之前,可能要冻结 Object.prototype,明确指定所有的选项,或者通过 Object.create(null) 将 proto (en-US) 属性指向 null。

    • 继承属性

      • 如果访问者的属性是被继承的,它的 get 和 set 方法会在子对象的属性被访问或者修改时被调用。如果这些方法用一个变量存值,该值会被所有对象共享。

        const myClass = () => {}
        
        let name = 'xbd'
        Object.defineProperty(myClass.prototype, 'name', {
         get() {
           return name
         },
         set(newValue) {
           name = newValue
         }
        })
        
        const a = new myClass()
        const b = new myClass()
        console.log(a.name) // 输出 xbd
        b.name = 'xbd2'
        console.log(a.name) // 输出 xbd2
        
      • 这可以通过将值存储在另一个属性中解决。在 get 和 set 方法中,this 指向某个被访问和修改属性的对象。

        const myClass = () => {}
        
        Object.defineProperty(myClass.prototype, 'name', {
          get() {
            return this.name
          },
          set(newValue) {
            this.name = newValue
          }
        })
        
        const a = new myClass()
        const b = new myClass()
        b.name = 'xbd'
        console.log(a.name) // 输出 undefined
        console.log(b.name) // 输出 xbd
        
      • 不像访问者属性,值属性始终在对象自身上设置,而不是一个原型。然而,如果一个不可写的属性被继承,它仍然可以防止修改对象的属性。

        const myClass = () => {}
        
        myClass.prototype.name = 'myClass'
        Object.defineProperty(myClass.prototype, 'age', {
          writable: false,
          value: 18
        })
        
        const a = new myClass()
        const b = new myClass()
        console.log(a.name) // 输出 myClass
        a.name = 'xbd'
        console.log(a.name) // 输出 xbd
        
        console.log(b.age) // 18
        b.age = 20 // 继承了 writable 为 false ,故不可修改