從Vue源碼學習JavaScript之Object.defineproperty

Vue裏面有個耳熟能詳的詞是Object.defineproperty,這篇文章就介紹一下這個屬性。函數

Object.defineProperty() 方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 並返回這個對象。

語法

Object.defineProperty(obj, prop, descriptor)this

參數

  • obj:要在其上定義屬性的對象
  • prop:要定義或者要修改的屬性
  • descriptor:將被定義或修改的屬性描述符

descriptor屬性描述符

屬性描述符又可分爲數據描述符和存取描述符,能夠用getOwnPropertyDescriptors或者getOwnPropertyDescriptor獲取到屬性描述

數據描述符和存取描述符共有的屬性包括:code

  • configurable對象

    當且僅當該屬性的 configurable 爲 true 時,該屬性描述符纔可以被改變,同時該屬性也能從對應的對象上被刪除,若是爲false,則不能刪除或修改writable, configurable, enumerable。默認爲 true。繼承

    var animal = {
        name: 'cat'
    }
    console.log(Object.getOwnPropertyDescriptors(animal)) 
    //name: {value: "cat", writable: true, enumerable: true, configurable: true}
    console.log(animal.name) //cat
    delete animal.name
    console.log(animal.name) //undefined
    
    Object.defineProperty(animal, 'name', {
        value: 'dog',
        configurable: false
    })
    console.log(Object.getOwnPropertyDescriptors(animal))
    //name: {value: "dog", writable: false, enumerable: false, configurable: false}
    console.log(animal.name) //dog
    delete animal.name
    console.log(animal.name) //dog

    能夠看到,configurable默認屬性是true,設置爲false以後,delete對象的屬性將失效ip

須要注意的是,若是不是經過defineproperty定義的屬性,描述符默認值都是true;經過defineproperty定義的屬性,描述符默認是false
  • enumerableci

    當且僅當該屬性的enumerable爲true時,該屬性纔可以出如今對象的枚舉屬性中(for...in, Object.keys())。默認爲 true。get

    let animal = {
        name: 'cat'
     }
    for (let i in animal) {
        console.log(animal[i]) //cat
    }
    Object.defineProperty(animal, 'name', {
        enumerable: false
    })
    for (let i in animal) {
        console.log(animal[i]) //無輸出
    }

數據描述符其他屬性:it

  • value

    該屬性對應的值。能夠是任何有效的 JavaScript 值(數值,對象,函數等)。默認爲 undefined。io

  • writable

    當且僅當該屬性的writable爲true時,value才能被賦值運算符改變。默認爲 true。

存取描述符其他屬性:

  • get

    一個給屬性提供 getter 的方法,若是沒有 getter 則爲 undefined。當訪問該屬性時,該方法會被執行,方法執行時沒有參數傳入,可是會傳入this對象(因爲繼承關係,這裏的this並不必定是定義該屬性的對象)。

    let animal = {}
    let name = 'cat'
    Object.defineProperty(animal, 'name', {
      value: 'cat',
      get: function () {
        return name
      }
    })
    //報錯:Uncaught TypeError: Invalid property descriptor. Cannot both specify accessors and a value or writable attribute, #<Object>
    
    let animal = {}
    let name = 'cat'
    Object.defineProperty(animal, 'name', {
        get: function () {
          return name
        }
    })
    console.log(animal.name) //cat
若是一個描述符不具備value,writable,get 和 set 任意一個關鍵字,那麼它將被認爲是一個數據描述符。若是一個描述符同時有(value或writable)和(get或set)關鍵字,將會產生一個異常。
  • set

    一個給屬性提供 setter 的方法,若是沒有 setter 則爲 undefined。當屬性值修改時,觸發執行該方法。該方法將接受惟一參數,即該屬性新的參數值。

    let animal = {}
    let name = 'cat'
    Object.defineProperty(animal, 'name', {
        get: function () {
          return name
        },
        set: function (val) {
          name = val
        }
    })
若是訪問者的屬性是被繼承的,它的 get 和set 方法會在子對象的屬性被訪問或者修改時被調用。若是這些方法用一個變量存值,該值會被全部對象共享。

能夠藉助中間值來解決

let animal = {}
    let name = 'cat'
    Object.defineProperty(animal, 'name', {
        get: function () {
          return this.stored_x
        },
        set: function (val) {
          this.stored_x = val
        }
    })
相關文章
相關標籤/搜索