javascript之Object.defineProperty的奧妙


直切主題

今天遇到一個這樣的功能:

  • 寫一個函數,該函數傳遞兩個參數,第一個參數為返回對象的總數據量,第二個參數為初始化對象的數據。如:
var o = obj (4, {name: 'xu', age: 21})   //  返回了一個能容納4條數據的對象,初始數據為name:'xu'和age: 21
  • 返回的該對象總會有以下屬性:overLength(數據容納量)、size(當前數據條數)
  • 返回的對象應該有以下方法:cache(保存一條數據)、delete(刪除一條數據)
  • 每一次引用某屬性后,該屬性值消失。如:
o.name // xu
o.name //undefined
o.size //1

那么問題來了,如何設置屬性在使用一次后自動消除呢,經過思考,想到了,get和set方法,get和set又是什么呢,如下:

var o = {
  _id: 11,
  get id () {
    return this._id + '_get'
  },
  set id (value) {
    this._id = value + '_set'
  }
}

console.log(o.id)  // 11_get
o.id = '666'
console.log(o.id)  // 666_set_get

js中引用屬性的值視為get,給屬性賦值視為set,get和set方法可以在獲取/設置屬性值時進行一定的操作。

本文中所用到了get和set方法,不過並不是上述例子那樣簡單的在對象中書寫,我們借助了本文的主角Object.defineProperty方法。

什么是Object.defineProperty

Object.defineProperty是es5新加的給對象屬性設置描述符的方法,使用方法如下:

Object.defineProperty(obj, prop, descriptor)  // 對象、屬性、描述符

基本的描述符有3個:

  • writable  --  是否為可寫
  • configurable  --  是否為可配置的
  • enumerable  --  是否為可枚舉的

下面分別舉個簡單的例子來說明其用法

writable

顧名思義,設置屬性是否為可寫,如果是false,則屬性之后的賦值操作無效

var o = {
  name: 'xu'
}
Object.defineProperty(o, 'name', {
  writeable:false
})
o.name = 'lee'
console.log(o.name)  //xu

configurable

屬性是否可配置,設置為false后,該屬性不可被刪除,也不可再更改為可配置的,但是可以從可寫改為不可寫

var o = {
  name: 'xu'
}
Object.defineProperty(o, 'name', {
  configurable:false
})
o.name = 'lee'
console.log(o.name)  // lee   不可配置可寫
delete o.name
console.log(o.name)  //lee  刪除失敗
Object.defineProperty(o, 'name', {
  writable: false
})
o.name ='li'
console.log(o.name)  // lee  不可配置不可寫

enumerable

屬性是否可枚舉,如果是false,則屬性不可枚舉,不可枚舉屬性對  for ... in語句和Ojbect.keys是不可見的。

var o = {
  name: 'xu',
  age: 21
}
Object.defineProperty(o, 'name', {
  enumerable: false
})
console.log(Object.keys(o)) //["age"]

分界線----------------------------------------------------------------------------

除了上面三個屬性,defineProperty方法內部還可以定義屬性的value值, get/set方法,那么本章則用到了使用Object.defineProperty方法定義屬性的get/set值。

問題解答

思路:本題主要圍繞着屬性的get/set做進一步處理,要求引用一次屬性值后,屬性值消失(undefined),那么就需要在get方法中做文章,所以我們先制定一個定義屬性描述符的方法,我把它定義成這樣:

var _defineProperty = function (ret, key) {
    Object.defineProperty(ret, key, {
      set: function (value) {
        //  針對 o.key = value 的set方法
        if (!datas[key]) {
          ret.size++
        }
        datas[key] = value
      },
      get: function () {
        //  獲取當前數據,如果當前數據有值,返回值並將當前屬性值設置為undefined
        var res = undefined
        if (typeof datas[key] !== undefined) {
          res = datas[key]
          ret.size--
          datas[key] = undefined
        }
        return res
      }
    })
  }

本題的主要部分就在這里,這段代碼利用的屬性的get和set方法,針對題目的要求,做出了這個功能函數。注意:返回的對象ret並沒有保存數據,他只是通過set和get方法對datas緩存對象中的數據進行的設置和讀取。

datas對象是這樣的,他利用的es6中的Object.assign方法,把init拷貝了一份作初始化

//  緩存數據
  var datas = Object.assign({}, init)

整個代碼就在這里,也很簡單,寫的也比較亂。如果有問題,請指出。

function FirstVaild (overLength, init) {

  //  定義最終返回對象,初始兩個值,一個對象總容量,一個是當前長度
  var ret = {
    overLength: overLength, 
    size: Math.min(Object.keys(init).length, overLength)  //如果傳入初始數據條數大於容量,取容量值
  }
  //  緩存數據
  var datas = Object.assign({}, init)

  //  定義屬性的函數
  var _defineProperty = function (ret, key) {
    Object.defineProperty(ret, key, {
      set: function (value) {
        //  針對 o.key = value 的set方法
        if (!datas[key]) {
          ret.size++
        }
        datas[key] = value
      },
      get: function () {
        //  獲取當前數據,如果當前數據有值,返回值並將當前屬性值設置為undefined
        var res = undefined
        if (typeof datas[key] !== undefined) {
          res = datas[key]
          ret.size--
          datas[key] = undefined
        }
        return res
      }
    })
  }

  //  將dirty初始化false,並定義每個屬性的get/set
  Object.keys(init).slice(0,ret.size).map(function (key) {
    _defineProperty(ret, key)
  })


  Object.assign(ret, {
    cache: function (key ,value) {
      if (this.size >= this.overLength) {
        throw '內存已滿,請擴展容量'
      }
      //  屬性不存在,定義他,並且長度+1, 屬性存在但值不存在,長度+1,
      if (!(key in datas)) {
        _defineProperty(this, key)
        this.size++
      } else if (!datas[key]) {
        this.size++
      }
      //  不論怎樣,新值覆蓋舊值
      datas[key] = value
      return this
    },
    //  刪除屬性直接把datas中的屬性設置為undefined
    delete: function (key) {
      datas[key] = undefined
      this.size--
      return this
    }
  })
  return ret
}

本章就到這里,博主為一介前端菜鳥,並沒有多少知識,try my best,寫點基礎知識,分享給正在學習前端的新人們。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM