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又是什麼呢,以下:es6

複製代碼
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個:this

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

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

writable

顧名思義,設置屬性是否爲可寫,若是是false,則屬性以後的賦值操做無效spa

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

configurable

屬性是否可配置,設置爲false後,該屬性不可被刪除,也不可再更改成可配置的,可是能夠從可寫改成不可寫code

複製代碼
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,寫點基礎知識,分享給正在學習前端的新人們。

相關文章
相關標籤/搜索