ES6 :Set 集合与 Map 集合

413 阅读6分钟

ES6 标准前,可选的集合类型有限,数组使用的又是数值型索引,经常用于队列和栈。ES6 新增 Set 集合和 Map 集合,开发者就可以使用它们作非数值型索引的集合。

Set 集合

基本操作

ES6 中 Set 类型是一种有序列表,含有相互独立的非重复值,通过 Set 集合有效地追踪各种离散值。

//  创建Set集合并添加元素
var set = new Set();
set.add(5);
set.add("5");
console.log(set.size);  //  2

//  创建Set集合并初始化
var set = new Set([1,2,3]);

//  非重复性
var set = new Set([1,2,2,3,3]);
console.log(set.size);  //  3
set.add(1);             //  已有,添加不成功
console.log(set.size);  //  3

//  检查集合是否含有指定元素
var set = new Set([1,2,3]); 
console.log( set.has(2) );      //  true
console.log( set.has(4) );      //  false

//  移除元素
var set = new Set([1,2,3]);
console.log(set.size);      //  3
set.delete(1);              //  删除成功返回true
console.log(set.size);      //  2
set.clear();
console.log(set.size);      //  0

为 Set 集合添加元素时,5 和 "5" 可以作为两个独立元素存在的,引擎内部通过 Object.is() 方法来检测两个值是否一致(Object.is()方法是 ES6 对象扩展的功能)。注意:+0 和 -0 被认为是相等的。
构造函数可接受所有可迭代对象作为参数,数值、Set集合、Map集合都是可迭代的。
非重复性:构造函数会过滤重复的值,保持集合元素唯一性,添加已有元素的行为会被忽略。
移除元素:有两种方式,delete() 删除指定元素,clear() 清空集合元素。

forEach 方法

使用过数组都应该知道数组也有 forEach 方法,可以简化数组遍历过程,不需要自己写循环语句。ES6 为 Set 集合也添加同样的方法。

类型数组Set集合
forEach()
回调函数的参数
1.元素值
2.下标索引
3.数组本身
1.元素值
2.元素值
3.Set集合本身

由于 Set 集合没有键名,ES6 标准委员会为了与数组的 forEach() 保持一致的三个参数,所以第一个参数与第二个参数相同,都能表示元素值。

var set = new Set([1,2,3]);
var fn = function (value){
    console.log(value)
}

set.forEach( fn );

Set 集合转数组

数组转 Set 集合很简单,只需给 Set 构造函数传入数组即可;Set 集合转数组,通过 ES6 新增加的展开运算符(...)就可以轻松解决,展开运算符也可以将可迭代对象转换为数组。

//  数组转 Set集合
var array = [1,2,3,4];
var set = new Set(array);
console.log(set);       //  Set(4) [ 1, 2, 3, 4 ]

//  Set集合转数组
var set = new Set([1,2,3]);
var array = [...set];
console.log(array);     //  Array(3) [ 1, 2, 3 ]

Weak Set 集合

Set 集合可以看作是一个强引用性的集合,如果我们存储的是对象的话,那 Set 集合就会强引用对象。如果这个对象被废弃,但是 Set 集合还引用了该对象,垃圾回收机制就不能释放该对象内存空间。
如果想要垃圾回收机制能处理对象,则首先要切断 Set 集合中对象的引用,才能再切断变量对对象引用。或者使用 clear() 清空,显然这是不理智的。

var object = {};
var set = new Set([object]);
console.log(set.size);      //  1
set.delete(object);
console.log(set);           //  0
object = null;

上面的代码能切断Set集合中对象的引用,但在操作麻烦,object置null前,要先用其切断Set对象引用。

ES6 早已为我们想好了解决的办法,就是另一种集合:Weak Set,它是一种弱引用的 Set 集合。Weak Set 集合只能用于存储对象,存储非对象程序抛出错误。
Weak Set 集合只支持3个方法:add()、has()、delete(),这三个方法与 Set 集合用法一致。

var weakSet = new WeakSet([1,{}]);  //  抛出错误

var weakSet = new WeakSet([{}]);
weakSet.add(1);     //  抛出错误

var object = {};
var weakSet = new WeakSet([object]);
object = null;
console.log(weakSet.size);      //  undefined,注意WeakSet没有这个属性

Weak Set 集合与 Set 集合的差别:

  1. Weak Set 保存对象弱引用且只能存储对象;
  2. Weak Set 只有add()、has()、delete()方法且不支持size属性;
  3. Weak Set 不可迭代、不暴露任何迭代器,所以 for-of、forEach() 无法使用。

Map 集合

Map 类型是一种存储许多键值对的有序列表。键名通过 Object.is() 方法实现键的唯一性。

基本操作

//  创建Map集合并添加键值对
var map = new Map();
map.set("key","value");
console.log(map.get("key"));        //  value

//  创建Map集合并初始化
var map = new Map([["key1","value1"], ["key2","value2"]]);
console.log(map.get("key1"));       //  value1
console.log(map.get("key2"));       //  value2

//  非重复性
var map = new Map([["key","value1"], ["key","value2"]]);
console.log(map.get("key"));        //  value2

//  检查集合是否含有指定的键
var map = new Map([["key1","value1"]]);
console.log( map.has("key1") );     //  true
console.log( map.has("key") );      //  false

//  移除键值对
var map = new Map([["key1","value1"], ["key2","value2"]]);
console.log(map.size);      //  2
map.delete("key1");         //  删除成功返回true
console.log(map.size);      //  1
map.clear();
console.log(map.size);      //  0

Map 集合 has()、delete()、clear()方法都与 Set 集合用法类似。
Map 构造函数传入数组来初始化,数组每个元素都是一个子数组,包含键与值两个元素。

forEach 方法

Map 集合使用 forEach() 遍历过程,按照键值对插入 Map 集合的顺序来传入回调函数中。

类型数组Map集合
forEach()
回调函数的参数
1.元素值
2.下标索引
3.数组本身
1.对应值
2.键名
3.Map集合本身

Weak Map 集合

Weak Map 是弱引用 Map 集合,用于键名存储对象的弱引用。Map 集合中,对象可用作键名,键名强引用对象。其实 Weak Map 和 Map 之间的道理与 Weak Set 和 Set的类似,围绕都是对象弱引用与强引用。
ES6 规定 Weak Map集合是一种存储许多键值对的无序列表,列表键名必须是非 null 类型的对象。

var weakMap = new WeakMap([[1, "a"]]);  //  抛出错误

var weakMap = new WeakMap();
weakMap.set(1,{});     //  抛出错误

var object = {};
var weakMap = new WeakMap([[object, { name:1 }]]);
object = null;
console.log(weakSet.size);      //  undefined,注意WeakSet没有这个属性

Weak Map 集合与 Map 集合的差别:

  1. Weak Map 键名保存对象弱引用且键名只能是对象;
  2. Weak Map 只有get()、set()、has()、delete()方法,但这些方法用法与 Map类似;
  3. Weak Map 不支持size属性;
  4. Weak Map 的 forEach() 方法无法使用。

相对 Map 集合而言,Weak Map 集合对用户的可见度更低。如果只用对象作为集合的键名,那么Weak Map 集合是最好的选择,通常保存一些对象的附加信息或私有数据。使用 Weak Map 有效避免内存泄漏的问题,优化内存使用。



如有错误,欢迎到评论区提出,谢谢。