js–對象內部屬性與 Object.defineProperty()

前言

    JavaScript 中允許使用一些內部特性來描述屬性的特徵,本文來總結一下對象內部屬性與 Object.defineProperty() 的相關知識。

正文

  1、屬性類型

  js中使用某些內部屬性來描述屬性的特徵,比如描述屬性是否可以枚舉,是否可以修改等特徵,我們無法訪直接問屬性的這些特徵,但是可以通過[[]]的方式來將某個特性標識為內部屬性。這些內部屬性分為數據屬性和訪問器屬性。

  (1)數據屬性

  數據屬性包含一個保存數據值的位置。值會從這個位置讀取,也會寫入到這個位置。數據屬性有 4個特性描述它們的行為。

  a、[[Configurable]]表示能否通過 delete 刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把數據屬性修改為訪問器屬性,默認 true。

  b、[[Enumerable]]表示能否通過 for-in 循環遍歷出屬性  默認 true。

  c、[[Writable]]表示能否修改屬性的值,默認 true,設置為 false 指改屬性只讀。

  d、[[Value]]保存這個屬性的數據值,讀寫屬性的值都是在這個位置,默認值為 undefined。

  要修改對象默認數據屬性,必須使用es5的 Object.defineProperty(),該方法包含三個參數,給其添加屬性的對象、屬性的名稱和一個描述符對象。如下:

    var person = {};
    Object.defineProperty(person, "age", {
      enumerable: true,
      configurable: false,
      value: 18
    });
    console.log(person.age);// 18
    delete person.age
    console.log(person.age);// 18

    Object.defineProperty(person, "height", {
      enumerable: false,
      value: 50
    });
    console.log(person);//{age:18,height:50}
    for (const key in person) {
      console.log(key, person[key]);// age 18
    }
    Object.defineProperty(person, "name", {
      writable: false,
      value: "Nicholas"
    });
    console.log(person.name); // "Nicholas"
    person.name = "Greg";
    console.log(person.name); // "Nicholas"

  注意:在調用 Object.defineProperty() 時, configurable 、 enumerable 和 writable 的值如果不指定,則都默認為 false 。

  (2)訪問器屬性

  訪問器屬性 ==訪問器屬性不包含數據值,他們包含setter和getter函數,這兩個函數並不是必須的

  a、[[Configurable]]表示能否通過delete刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把數據屬性修改為訪問器屬性,默認 true。

  b、[[Enumerable]]表示能否通過for-in循環遍歷出屬性  默認 true。

  c、[[Set]]寫入屬性的函數,默認 undefined。

  d、[[Get]]讀取屬性的函數,默認 undefined。

  同樣,訪問器屬性是不能直接定義的,必須使用 Object.defineProperty() 。如下:

    var person = { name: "Nicholas", isAdult: true };
    Object.defineProperty(person, 'age', {
      get: function () {
        return this.age
      },
      set: function (newValue) {
        this.isAdult = newValue >= 18 ? true : false
      }
    });
    console.log(person.isAdult);
    person.age = 17
    console.log(person.isAdult);

  注意:設置了訪問器屬性之後不能再設置數據屬性,比如設置了 set/get  就不能再設置 writable。

  2、補充:內部屬性的操作

  (1)一次性定義多個屬性

  ECMAScript 提供了 Object.defineProperties() 方法可以通過多個描述符一次性定義多個屬性。它接收兩個參數:要為之添加或修改屬性的對象和另一個描述符對象,其屬性與要添加或修改的屬性一一對應。這樣所有屬性都是同時定義的,並且數據屬性的configurable 、 enumerable 和 writable 特性值都是 false。
    var person = {}
    Object.defineProperties(person, {
      'name': {
        value: "Nicholas"
      },
      'isAdult': {
        writable: true,
        configurable: true,
        value: true
      },
      'age': {
        configurable: true,
        get() {
          return this.age
        },
        set(newValue) {
          this.isAdult = newValue >= 18 ? true : false
        }
      }
    })
    console.log(person.isAdult);// true
    person.age = 17// 首先到age的set方法,然後訪問isAdult的writable屬性並修改value值
    console.log(person.isAdult);// false

  (2)讀取屬性的特性   Object.getOwnPropertyDescriptor()獲取傳入對象給定屬性的描述符。Object.getOwnPropertyDescriptors()獲取給定對象的全部屬性描述符。

    var person = { name: "Nicholas", isAdult: true };
    Object.defineProperty(person, 'age', {
      get: function () {
        return this.age
      },
      set: function (newValue) {
        this.isAdult = newValue >= 18 ? true : false
      }
    });
    var descriptor = Object.getOwnPropertyDescriptor(person, 'age')
    console.log(descriptor);
    //configurable: false
    // enumerable: false
    // get: ƒ ()
    // set: ƒ (newValue)
    console.log(Object.getOwnPropertyDescriptors(person));
    // {name:{...},isAdult:{...},age:{...}}

  3、對象內部特性和 Object.defineProperty() 應用

  (1)手寫實現 const

    function myConst(key, value) {
      window.key = value // 把要定義的key掛載到window下,並賦值value
      Object.defineProperty(window, key, {
        enumerable: false,
        configurable: false,
        get: function () {
          return value
        },
        set: function (data) {
          if (data !== value) { // 當要對當前屬性進行賦值時,則拋出錯誤!
            throw new TypeError('Assignment to constant variable.')
          } else {
            return value
          }
        }
      })
    }
    myConst('a', 2)
    console.log(a);
    a = 3//報錯 :Assignment to constant variable.

  (2)手動實現每次訪問一個屬性時,值加一,使得a==1&&a==2&a==3成為可能

    window.b = 0
    Object.defineProperty(window, "a", {
      get() {
        this.b++
        return this.b
      },
      set(value) {
        this.b = value
      }
    })
    if (a == 1 && a == 2 & a == 3) {
      console.log("111");
    }//111

寫在最後

  以上就是本文的全部內容,希望給讀者帶來些許的幫助和進步,方便的話點個關注,小白的成長之路會持續更新一些工作中常見的問題和技術點。