Set 和 Map 数据结构

244 阅读4分钟

Set

Set 是一种类似于数组,但成员的值都是唯一的,没有重复的数据结构。数组去重:[... new Set(array)]

向 Set 加入值时不会发生类型转换,所以 5 和 "5" 是两个不同的值。Set 内部判断两个值是否相同时,使用的是类似于全等运算符( === ),不同的是 Set 结构对于两个 NaN 的判断是相等的。另外,两个对象总是不相等的。

let  set1 = new Set();
let a = NaN;
let b = NaN;
set1.add(a);
set1.add(b);
set1 // { NaN }

let set2 = new Set();
set2.add({});
set2.size // 1
set2.add({});
set2.size // 2

Set 实例的属性和方法

Set 实例有以下属性:

  • Set.prototype.constructor:构造函数,默认就是 Set 函数
  • Set.prototype.size:返回 Set 实例的成员总数

Set 实例的方法有两种:操作方法和遍历方法

操作方法:

  • add(value):添加某个值,返回 Set 结构本身
  • delete(value):删除某个值,返回一个布尔值,表示删除是否成功
  • has(value):返回一个布尔值,表示参数是否为 Set 的成员
  • clear():清除所有成员,没有返回值

遍历方法:

Set 的遍历顺序就是插入顺序。Set 只有键值,所以 keys 和 values 方法的行为一致,entries 返回的每个数组中为两个成员相等的键值

  • keys():返回键名的遍历器
  • values():返回键值的遍历器
  • etries():返回键值对的遍历器
  • forEach():使用回调函数遍历每个成员
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']

另外,借助数组的 map 和 filter 方法可以使 Set 用处增强

let set = new Set([1, 2, 3]);
set = new Set([...set].map(x => x * 2)); // { 2, 4, 6 }

let set = new Set([1, 2, 3, 4, 5]);
set = new Set([...set].filter(x => (x % 2) == 0)); // { 2, 4 }

利用 Set 可以实现并集、交集、差集

let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);

let union = new Set([...a, ...b]); // 并集,{ 1, 2, 3, 4 }
let intersect = new Set([...a].filter(x => b.has(x))); // 交集,{ 2, 3 }
let difference = new Set([...a].filter(x => !b.has(x))); // 差集,{ 1 }

WeakSet

与 Set 结构的主要区别:

  • 成员只能是对象,不能是其他类型的值
  • 成员都是弱引用。如果其他对象不再引用成员中的对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象是否还存在于 WeakSet 中。
  • 不可遍历成员,没有 size 属性
  • 没有 clear 方法
const ws = new WeakSet();
ws.add(1) // TypeError:Invalid value used in weak set
ws.add([1]) // TypeError:Invalid value used in weak set
ws.add([[1]]) // WeakSet {[1]}

成为 WeakSet 的成员的是数组的成员,而不是数组的本身,所以数组的成员必须是对象。

WeakSet 实例的方法

  • WeakSet.prototype.add(value):向 WeakSet 实例中添加成员
  • WeakSet.prototype.delete(value):清除 WeakSet 实例的指定成员
  • WeakSet.prototype.has(value):返回一个布尔值,表示某个值是否在 WeakSet 实例中

Map

Map 构造函数可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组。

const map = new Map([['name', '张三'], ['titiel', 'Author']]);
const errorMap = new Map(['name', '张三']); // TypeError

如果对同一个键多次赋值,后面的值会覆盖前面的值。

const map = new Map();
map.set(['a'], 555);
map.get(['a']); // undefined,因为两个['a']不同

const map1 = new Map();
const k1 = ['a'];
const k2 = ['a'];
map
.set(k1, 111)
.set(k2, 222);
map.get(k1); // 111
map.get(k2); // 222

Map 的键是和内存地址绑定的,只要内存地址不一样,就视为两个键。当 Map 的键是简单类型的值时,只要两个值严格相等(NaN 除外,Map 将 NaN 视为同一个键),就视为同一个键。

Map 实例的属性和方法

属性:

  • size 属性:size 属性返回 Map 结构的成员数

操作方法:

  • set(key, value):设置 key 对应的键值,返回整个 Map 结构,因此 set 方法可以采用链式写法。如果 key 已经有值,则会更新键值
  • get(key):读取 key 对应的键值,如果找不到 key,返回 undefined
  • has(key):返回一个布尔值,表示 key 是否有对应的键
  • delete(key):删除某个键,返回 true 表示删除成功,返回 false 表示删除失败
  • clear():清除所有成员,没有返回值

遍历方法:

  • keys():遍历返回键名
  • values():遍历返回键值
  • entries():遍历返回所有键值对
  • forEach():遍历所有成员
let map = new Map([['F', 'no'], ["T", 'yes']]);
for(let key of map.entries()) {
    console.log(key);
}
// ['F', 'no']
// ['T', 'yes']

WeakMap

与 Map 的主要区别:

  • 只接受对象作为键名(null 除外),不接受其他类型值作为键名
  • WeakMap 的键名所指向的对象不计入垃圾回收机制
  • 不可遍历成员,没有 size 属性
  • 没有 clear 方法
const wm = new WeakMap();
let key = {};
let obj = { foo: 1 };

wm.set(key, obj);
obj = null;
wm.get(key); // { foo: 1 }

WeakMap 实例的方法

  • get()
  • set()
  • has()
  • delete()