直切主題
今天遇到一個這樣的功能:
- 寫一個函數,該函數傳遞兩個參數,第一個參數為返回對象的總數據量,第二個參數為初始化對象的數據。如:
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,寫點基礎知識,分享給正在學習前端的新人們。