ES6的js新增了Set集合,为这门语言带来了集合数据结构,为了更深层次的理解Set集合底层封装机制,我们来手写一个Set集合。
首先要知道Set集合的内置方法和属性有哪些?
Set内置一个类似对象数组的一种数据结构,我们就用set数组来代替,同时还有一个size属性指向集合的总个数。
其他内置方法包括:增、删、查、遍历
接下来我们开始写代码:
'use strict'
//迭代器
const mySet = function () {
const wm = new WeakMap();
const InitPrivate = Symbol('private');
function mySet(args) {
this.set = [];
// this.size=this.get();
this[InitPrivate](args);
Object.defineProperties(this.set,{
[Symbol.iterator]:{
value:function(){
return this;
}
},
next:{
value:function(){
if(wm.get(this)[this.index]<this.set.length){
return {value:this.set[wm.get(this)[this.index]++].value,done:false}
}else{
wm.get(this)[this.index]=0;
return {value:undefined,done:true};
}
}.bind(this)
},
});
Object.defineProperty(this,"size",{
value:0,
enumerable:false,
writable:true,
configurable:false,
})
wm.get(this)[this.verIf]();
}
//初始化私有属性和方法
mySet.prototype[InitPrivate] = function (args) {
Object.defineProperties(this,{
verIf:{
value:Symbol('private')
},
index:{
value:Symbol('private')
}
})
const privateMembers = wm.get(this) || {};
privateMembers[this.index]=0;
privateMembers[this.verIf] = Init.bind(this, args);
wm.set(this, privateMembers);
}
//对初始赋值进行检测
function Init(args) {
args = args || [];
if (!(args instanceof Array)) {
throw new Error('参数必须是数组');
}
for (const value of args) {
if (!this.has(value)) {
this.set.push({ value: value });
}
}
this.size=this.get();
}
Object.defineProperties(mySet.prototype, {
delete: {
value: function (data) {
return this.set.some((item, i) => {
if (item.value === data) {
this.set.splice(i, 1);
this.size=this.get();
return true;
}
});
}
},
add: {
value: function (data) {
if (!this.has(data) && data) {
this.set.push({ value: data });
}
this.size=this.get();
//返回this,以便链式调用
return this;
}
},
has: {
value: function (data) {
return this.set.some(item => item.value === data);
}
},
clear:{
value:function(){
this.set.splice(0,this.set.length);
this.size=this.get();
}
},
get:{
value:function(){
return this.set.length;
}
},
entries:{
value:function(){
let Entries=[];
for(const value of this){
Entries.push({key:value,value:value});
}
return Entries;
}
},
values:{
value(){
let SetIterator=[];
this.set.forEach(element => {
SetIterator.push(element);
});
return SetIterator;
}
},
[Symbol.iterator]:{
value:function(){
return this.set;
}
},
})
return mySet;
}();
在封装Set的时候我发现,js原生Set集合对象是能够被迭代的,由此可见内部一定是部署了一个迭代器的,于是我也在mySet集合中部署了迭代器,让所有mySet的对象的迭代器都指向内部的this.set,而且使用了一个私有属性index,用处就是让同一个对象可被重复迭代;
写完上述代码后,你可以通过实例对象对里面的方法进行检查,接下里我们用自己实现的Set集合来实现集合交并差的操作:
//交集
let overSet=Array.from(s).filter(item=>x.has(item));
console.log(overSet); //[2, 3, 4]
//并集
let andSet=[...new mySet([...s,...x])];
console.log(andSet); //[1, 2, 3, 4, 6]
//差集
let diffSet=Array.from(s).filter(item=>!x.has(item));
console.log(diffSet); //[1]
可以发现和原生集合是一模一样的。