ES6之Set集合与Map集合

212 阅读5分钟

ES6 标准前,数组一直是JavaScript中常用的数值型索引的集合类型,经常用于队列和栈。而 ES6 新增 Set 集合和 Map 集合,开发者可以使用它们来创建非数值型索引的集合。

一、Set 集合

基本操作

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

  • 创建Set集合并添加元素
let set = new Set();
set.add(1);
set.add("1");
console.log(set.size);  //  2
  • 创建 Set 集合并初始化
let set = new Set([1,2,3]);
  • 非重复性
let set = new Set([1,2,2,3,3]);
console.log(set.size);  //  3
set.add(1);             //  已有,添加不成功
console.log(set.size);  //  3
  • 检查集合是否含有指定元素
let set = new Set([1,2,3]); 
console.log( set.has(2) );      //  true
console.log( set.has(4) );      //  false
  • 移除元素
let 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 集合添加元素时,1 和 "1" 可以作为两个独立元素存在的。注意:+0 和 -0 被认为是相等的。 构造函数 Set() 可接受所有可迭代对象作为参数,数值、Set集合、Map集合都是可迭代的。 非重复性:构造函数会过滤重复的值,保持集合元素唯一性,添加已有元素的行为会被忽略。 移除元素:有两种方式,delete() 删除指定元素,clear() 清空集合元素。

遍历操作

  • forEach()

forEach 方法相信大家在数组中用得比较多,可以用来简化数组遍历过程,不需要自己写循环语句。ES6 为 Set 集合也添加同样的方法。在数组中,forEach接受三个参数,分别是:元素值、元素索引、数组本身,由于 Set 集合没有键名,ES6 标准委员会为了与数组的 forEach() 保持一致的三个参数,所以第一个参数与第二个参数相同,都能表示元素值。

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

set.forEach( fn );

  • keys(),values(),entries()

keys方法、values方法、entries方法返回的都是遍历器对象。由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。

let set = new Set(['red', 'green', 'blue']);

for (let item of set.keys()) {
  console.log(item);
}
// red
// green
// blue

for (let item of set.values()) {
  console.log(item);
}
// red
// green
// blue

for (let item of set.entries()) {
  console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]

Weak Set 集合

WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有三个区别。

  • Weak Set 只能存储对象;
  • Weak Set 保存对象弱引用;
  • Weak Set 只有add()、has()、delete()方法且不支持size属性,不能遍历。
  1. WeakSet 的成员只能是对象,而不能是其他类型的值。
let ws = new WeakSet();
ws.add(1)// 报错
ws.add(Symbol())// 报错

上面代码尝试向 WeakSet 增加一个数值和Symbol值,结果报错,因为 WeakSet 只能放置对象。

  1. Set 集合是一个强引用性的集合,如果我们存储的是对象的话,那 Set 集合就会强引用对象。如果这个对象被废弃,但是 Set 集合还引用了该对象,垃圾回收机制就不能释放该对象内存空间。如果想要垃圾回收机制能处理对象,则首先要切断 Set 集合中对象的引用,才能再切断变量对对象引用。或者使用delete() 或 clear() 清空。
let object = {};
let set = new Set([object]);
console.log(set.size);      //  1

set.delete(object);
console.log(set);           //  0
object = null;

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

而ES6 新增的WeakSet 中对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。

let object = {};
let ws = new WeakSet([object]);
object = null;
console.log(ws.size);      //  undefined,注意WeakSet没有这个属性
  1. WeakSet 没有size属性,没有办法遍历它的成员。
let ws = new WeakSet();
ws.size // undefined
ws.forEach // undefined

ws.forEach(function(item){ 
    console.log('WeakSet has ' + item)}) //报错
}

二、Map 集合

ES6 之前,我们使用对象键值对只能用字符串当作键,这给我们使用带来了很大的限制,而 ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应。

let m = new Map();
let o = {p: 'Hello World'};//创建对象

m.set(o, 'content')
m.get(o) // 对象o可作为“键”,赋值"content"

基本操作

  • 创建Map集合并添加、获取键值对
let map = new Map();
map.set("key","value");
console.log(map.get("key"));        //  value
  • 创建Map集合并初始化
let map = new Map([["key1","value1"], ["key2","value2"]]);
console.log(map.get("key1"));       //  value1
console.log(map.get("key2"));       //  value2
  • 非重复性
let map = new Map([["key","value1"], ["key","value2"]]);
console.log(map.get("key"));        //  value2
  • 检查集合是否含有指定的键
let map = new Map([["key1","value1"]]);
console.log( map.has("key1") );     //  true
console.log( map.has("key") );      //  false
  • 移除键值对
let 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

遍历方法

和 Set 类似,Map 也有forEach()、keys()、values()、entries() 四个遍历的方法,需要注意的是,Map 的遍历顺序就是插入顺序。

onst map = new Map([
  ['F', 'no'],
  ['T',  'yes'],
]);

for (let key of map.keys()) {
  console.log(key);
}
// "F"
// "T"

for (let value of map.values()) {
  console.log(value);
}
// "no"
// "yes"

for (let item of map.entries()) {
  console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"

// 或者
for (let [key, value] of map.entries()) {
  console.log(key, value);
}
// "F" "no"
// "T" "yes"

// 等同于使用map.entries()
for (let [key, value] of map) {
  console.log(key, value);
}
// "F" "no"
// "T" "yes"

Weak Map 集合

WeakMap 结构与 Map 结构类似,也是用于生成键值对的集合。和 Set 和 Weak Set 的区别类似,前者之间也存在着以下几点差别:

  • Weak Map 只接受对象作为键名(null除外),不接受其他类型的值作为键名。
  • Weak Map 键名所指向的对象,不计入垃圾回收机制,即为弱引用对象。
  • Weak Map 没有遍历操作(即没有keys()、values()和entries()方法),也没有size属性。
let wm = new WeakMap();

// size、forEach、clear 方法都不存在
wm.size // undefined
wm.forEach // undefined
wm.clear // undefined