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_getjs中引用属性的值视为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) //xuconfigurable
属性是否可配置,设置为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 }