Set and Map

Set

Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。

NaN 是与 NaN 相等的(虽然 NaN !== NaN),剩下所有其它的值是根据 === 运算符的结果判断是否相等。

基本集合操作

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
39
40
41
42
43
44
45
46
47
48
49
50
let setA = new Set([1, 2, 3, 4]),
setB = new Set([2, 3]),
setC = new Set([3, 4, 5, 6]);

//包含
function isSuperSet(set, subset) {
for (let elem of subset) {
if (!set.has(elem)) {
return false
}
}
return true;
}

//交叉
function intersection(setA, setB) {
let _intersection = new Set();
for (let elem of setB) {
if (setA.has(elem)) {
_intersection.add(elem);
}
}

return _intersection;
}

//联合
function union(setA, setB) {
let _union = new Set(setA);
for (let elem of setB) {
_union.add(elem);
}

return _union;
}

//不是很完善
function difference(setA, setB) {
let _difference = new Set(setA);
for (let elem of setB) {
_difference.delete(elem);
}

return _difference;
}

console.log(isSuperSet(setA, setB)); // => true
console.log(union(setA, setC)); // => Set [1, 2, 3, 4, 5, 6]
console.log(intersection(setA, setC)); // => Set [3, 4]
console.log(difference(setA, setC)); // => Set [1, 2]

数组去重

1
2
const numbers = [2,3,4,4,2,3,3,4,4,5,5,6,6,7,5,32,3,4,5];
console.log([...new Set(numbers)]);

Map

Map 对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。

Map 是一个键值对的集合,很像 Object。但主要的区别是,Map 允许所有数据类型作为键。

1
2
3
4
5
6
7
8
9
10
11
12
let map = new Map();

map.set('1', 'str1'); // 字符串作为 key
map.set(1, 'num1'); // 数字作为 key
map.set(true, 'bool1'); // 布尔值作为 key

// 还记得普通对象 Object 吗?它将会吧所有的键转化为字符串类型
// 但是 Map 将会保留键的类型,所以下面这两个是不同的:
alert( map.get(1) ); // 'num1'
alert( map.get('1') ); // 'str1'

alert( map.size ); // 3

将NaN作为Map的键

1
2
3
4
5
6
7
let myMap = new Map();
myMap.set(NaN, "not a number");

myMap.get(NaN); // "not a number"

let otherNaN = Number("foo");
myMap.get(otherNaN); // "not a number"

Map 与 数组

使用常规的Map构造函数可以将一个二维键值对数组转换成一个Map对象

1
2
let kvArray = [["key1", "value1"], ["key2", "value2"]];
let myMap = new Map(kvArray); //Map(2){"key1" => "value1", "key2" => "value2"}

用 Object.entries(obj) 初始化map

1
2
3
4
let map = new Map(Object.entries({
name: "John",
age: 30
}));

使用Array.from函数可以将一个Map对象转换成一个二维键值对数组

复制Map

1
2
3
4
5
6
7
8
var original = new Map([
[1, 'one']
]);

var clone = new Map(original);

console.log(clone.get(1)); // one
console.log(original === clone); // false. Useful for shallow comparison 浅层拷贝

合并Map

1
2
3
4
5
6
7
8
9
10
11
12
13
let first = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three']
])

let second = new Map([
[1, 'uno'],
[2, 'dos']
]);
// 合并两个Map对象时,如果有重复的键值,则后面的会覆盖前面的。
// 展开运算符本质上是将Map对象转换成数组。
let merged = new Map([...first, ...second]);

WeakMap + WeakSet

WeakMap 他的键必须是对象, 不能是基础类型的值

相比于 Map 与 Set 的强引用,弱引用可以令对象在 “适当” 情况下正确被 GC 回收,减少内存资源浪费。

在弱引用的情况下,GC 回收时,不会把其视作一个引用;如果没有其他强引用存在,那这个对象将被回收

1
2
3
4
5
6
7
8
9
10
11
12
// 1. WeakMap 键必须是对象
const err = new WeakMap([['a',1]]); // TypeError: Invalid value used as weak map key

// 2. WeakMap/WeakSet 的弱引用
const wm = new WeakMap([[{'a':1},1]]); // Object {'a': 1} 会正常被 GC 回收

const ws = new WeakSet();
ws.add({'a':1}); // Object {'a': 1} 会正常被 GC 回收

const obj = {'b': 1};
ws.add(obj); // Object {'b': 1} 不会被正常 GC 回收,因为存在一个强引用
obj = undefined; // Object {'b': 1} 会正常被 GC 回收

淘宝前端团队