Set
对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用,值的集合
实现 先把程序骨架写好
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var MySet = (function ( ){ var symbolNaN = Symbol ('NaN' ); function _encode (val ){} function _decode (val ){} function _createIterator (list, iterator ){} function _Set (values ){ } _Set.prototype .has = function (val ){} _Set.prototype .delete = function (val ){} _Set.prototype .clear = function ( ){} _Set.prototype .entries = function ( ){} _Set.prototype .values = function ( ){} _Set.prototype .forEach = function (fn, context ) {} _Set.prototype [Symbol .iterator ] = function ( ){} return _Set })();
构造函数 构造函数接受一个迭代器对象,并将其顺序插入到集合中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function _forOf (obj, cb ){ if (typeof obj[Symbol .iterator ] !== 'function' ) throw new TypeError (obj + 'is not a iterable' ) if (typeof cb !== 'function' ) throw new TypeError ('cb is not a function' ) var iterable = obj[Symbol .iterator ]() var res while (true ){ res = iterable.next () if (res.done ) break cb (res.value ) } } function _Set (values ){ var self = this self._set = [] Object .defineProperty (self, 'size' , { enumerable : false , configurable : false , get ( ){ return self._set .length } }) _forOf (values, function (val ){ self.add (val) }) return self }
add 先判断是否存在,然后往里面写入,这里需要注意的一点是 NaN
的情况
NaN != NaN
这是什么大坑 通过 _encode
和 _decode
方法进行处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var symbolNaN = Symbol ('NaN' ); function _encode (val ){ return val !== val ? symbolNaN : val; } function _decode (val ){ return val === symbolNaN ? NaN : val; } _Set.prototype .add = function (val ){ if (!this .has (val)){ this ._set .push (_encode (val)) } return this }
has 1 2 3 4 _Set.prototype .has = function (val ){ return this ._set .indexOf (_encode (val)) !== -1 }
delete 1 2 3 4 5 6 7 8 9 10 _Set.prototype .delete = function (val ){ if (!this .has (val)) return false val = _encode (val) let i = this ._set .indexOf (val) this ._set .splice (i, 1 ) return true }
clear 1 2 3 4 _Set.prototype .clear = function ( ){ this ._set = [] }
entries & values 这两个方法是返回迭代器对象
迭代器 是一个对象,它定义一个序列,并在终止时可能返回一个返回值。 更具体地说,迭代器是通过使用 next()
方法实现 Iterator protocol 的任何一个对象,该方法返回具有两个属性的对象: value
,这是序列中的 next 值;和 done
,如果已经迭代到序列中的最后一个值,则它为 true
。如果 value
和 done
一起存在,则它是迭代器的返回值
简单带你说就是通过 next() 方法去访问下一个值,直到 done 为 true 停止
1 2 3 4 5 6 7 8 9 10 11 12 13 function _createIterator (list, iterator ){ var next = 0 var obj = { next : function ( ){ return next < list.length ? { done : false , value : iterator (list[next++]) } : { done : true , value : undefined } } } obj[Symbol .iterator ] = function ( ) { return obj } return obj }
有了这个生成迭代器方法,我们就可以根据规范去编写
1 2 3 4 5 6 7 8 _Set.prototype .entries = function ( ){ return _createIterator (this ._set , function (val ){ return [_decode (val), _decode (val)]}) } _Set.prototype .values = function ( ){ return _createIterator (this ._set , function (val ){ return _decode (val)}) }
注意 Set
对象也是一个迭代器对象
1 2 3 4 _Set.prototype [Symbol .iterator ] = function ( ){ return this .values (); }
forEach 1 2 3 4 5 6 7 _Set.prototype .forEach = function (fn, context ) { for (var i = 0 ; i < this ._set .length ; i++) { var val = _decode (this ._set [i]) fn.call (context, val, val, this ) } }
大功告成,简单写一个测试试试看 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 let arr = [1 ,2 ,3 ,4 ,NaN ,1 ,2 ,3 ,4 ,NaN ]let mySet = new MySet (arr)let set = new Set (arr)let myResult = []let result = []mySet.forEach ((...args ) => { myResult.push (args) }) set.forEach ((...args ) => { result.push (args) }) myResult.push ([...mySet]) result.push ([...set]) for (const val of set.entries ()) { result.push (val) } for (const val of set.values ()) { result.push (val) } for (const val of mySet.entries ()) { myResult.push (val) } for (const val of mySet.values ()) { myResult.push (val) } myResult.push (mySet.has (NaN )) myResult.push (mySet.delete (NaN )) myResult.push (mySet.has (NaN )) myResult.push (mySet.delete (NaN )) myResult.push (mySet) result.push (set.has (NaN )) result.push (set.delete (NaN )) result.push (set.has (NaN )) result.push (set.delete (NaN )) result.push (set) console .log (myResult)console .log (result)
完全一致!
源码地址