John Doe
Articles3
Tags4
Categories1

Categories

Archive

JS常用方法整理

JS常用方法整理

Map 和 Set 用法详解

一、Map(映射)

Map 是一个带键的数据项的集合,与 Object 类似,但允许任何类型的键

1. 创建和基本操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 创建 Map
let map = new Map();

// 设置值 - set(key, value)
map.set('name', '张三');
map.set(1, 'number one');
map.set(true, 'boolean');

// 获取值 - get(key)
console.log(map.get('name')); // '张三'
console.log(map.get(1)); // 'number one'

// 检查是否存在 - has(key)
console.log(map.has('name')); // true
console.log(map.has('age')); // false

// 删除 - delete(key)
map.delete('name');

// 获取大小 - size
console.log(map.size); // 2

// 清空 - clear()
map.clear();

2. 链式调用

set 方法返回 map 本身,可以链式调用:

1
2
3
4
let map = new Map();
map.set('a', 1)
.set('b', 2)
.set('c', 3);

3. 对象作为键(重要特性)

1
2
3
4
5
6
7
8
9
10
11
12
13
let john = { name: 'John' };
let ben = { name: 'Ben' };

let visitsCountMap = new Map();

visitsCountMap.set(john, 123);
visitsCountMap.set(ben, 456);

console.log(visitsCountMap.get(john)); // 123
console.log(visitsCountMap.get(ben)); // 456

// 注意:普通对象无法实现这个功能
// 因为对象的键会被转换为字符串 "[object Object]"

4. Map 迭代

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
let recipeMap = new Map([
['cucumber', 500],
['tomatoes', 350],
['onion', 50]
]);

// 遍历键 - keys()
for (let vegetable of recipeMap.keys()) {
console.log(vegetable); // cucumber, tomatoes, onion
}

// 遍历值 - values()
for (let amount of recipeMap.values()) {
console.log(amount); // 500, 350, 50
}

// 遍历键值对 - entries()
for (let entry of recipeMap.entries()) {
console.log(entry); // ['cucumber', 500], ['tomatoes', 350], ['onion', 50]
}

// 直接遍历(默认使用 entries)
for (let [key, value] of recipeMap) {
console.log(`${key}: ${value}`);
}

// forEach 方法
recipeMap.forEach((value, key, map) => {
console.log(`${key}: ${value}`);
});

重要:Map 保持插入顺序,迭代顺序与插入顺序相同。

5. 从对象创建 Map

1
2
3
4
5
6
7
8
9
10
let obj = {
name: 'John',
age: 30
};

// 使用 Object.entries()
let map = new Map(Object.entries(obj));

console.log(map.get('name')); // 'John'
console.log(map.get('age')); // 30

6. 从 Map 创建对象

1
2
3
4
5
6
7
8
9
10
11
12
let map = new Map([
['banana', 1],
['orange', 2],
['meat', 4]
]);

// 使用 Object.fromEntries()
let obj = Object.fromEntries(map.entries());
// 或简写
let obj2 = Object.fromEntries(map);

console.log(obj); // { banana: 1, orange: 2, meat: 4 }

7. Map 与 Object 对比

特性 Map Object
键类型 任意类型 字符串/Symbol
键顺序 保持插入顺序 不保证顺序
大小 size 属性 需手动计算
迭代 直接可迭代 Object.keys()
性能 频繁增删时更好 字面量创建更方便

二、Set(集合)

Set 是值的集合,每个值只能出现一次(自动去重)。

1. 创建和基本操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 创建 Set
let set = new Set();

// 添加值 - add(value)
set.add(1);
set.add(2);
set.add(3);
set.add(1); // 重复值会被忽略

console.log(set); // Set(3) {1, 2, 3}

// 检查是否存在 - has(value)
console.log(set.has(1)); // true
console.log(set.has(5)); // false

// 删除值 - delete(value)
set.delete(2);

// 获取大小 - size
console.log(set.size); // 2

// 清空 - clear()
set.clear();

2. 从数组创建(去重)

1
2
3
4
5
6
7
8
9
// 数组去重
let arr = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4];
let uniqueSet = new Set(arr);
let uniqueArr = [...uniqueSet];

console.log(uniqueArr); // [1, 2, 3, 4]

// 简写
let unique = [...new Set(arr)];

3. Set 迭代

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let set = new Set(['apple', 'orange', 'banana']);

// for...of 遍历
for (let value of set) {
console.log(value);
}

// forEach 遍历
// 注意:回调函数有三个参数,前两个相同(为了与 Map 兼容)
set.forEach((value, valueAgain, set) => {
console.log(value);
});

// 遍历方法(为了与 Map 兼容)
set.keys(); // 返回值的可迭代对象
set.values(); // 同 keys()
set.entries(); // 返回 [value, value] 的可迭代对象

4. Set 常见用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 1. 数组去重
function unique(arr) {
return [...new Set(arr)];
}

// 2. 字符串去重
let str = 'aabbccdd';
let uniqueStr = [...new Set(str)].join(''); // 'abcd'

// 3. 交集
let a = new Set([1, 2, 3]);
let b = new Set([2, 3, 4]);
let intersection = new Set([...a].filter(x => b.has(x)));
console.log([...intersection]); // [2, 3]

// 4. 并集
let union = new Set([...a, ...b]);
console.log([...union]); // [1, 2, 3, 4]

// 5. 差集
let difference = new Set([...a].filter(x => !b.has(x)));
console.log([...difference]); // [1]

三、WeakMap 和 WeakSet

WeakMap

  • 必须是对象
  • 键是弱引用,不阻止垃圾回收
  • 不可迭代,没有 sizekeys()values()entries() 方法
  • 只有 getsethasdelete 方法
1
2
3
4
5
6
7
8
let weakMap = new WeakMap();

let obj = { name: 'test' };
weakMap.set(obj, 'some value');

console.log(weakMap.get(obj)); // 'some value'

obj = null; // 对象被回收,weakMap 中的对应项也会被清除

应用场景:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 1. 额外数据存储
let userCache = new WeakMap();

function process(user) {
if (!userCache.has(user)) {
let result = /* 复杂计算 */;
userCache.set(user, result);
}
return userCache.get(user);
}

// 2. 私有数据
const privateData = new WeakMap();

class Person {
constructor(name) {
privateData.set(this, { name });
}

getName() {
return privateData.get(this).name;
}
}

WeakSet

  • 必须是对象
  • 值是弱引用
  • 不可迭代
  • 只有 addhasdelete 方法
1
2
3
4
5
6
7
8
let weakSet = new WeakSet();

let user = { name: 'John' };
weakSet.add(user);

console.log(weakSet.has(user)); // true

user = null; // 对象被回收

应用场景:

1
2
3
4
5
6
7
8
9
10
// 追踪访问过的对象
let visitedSet = new WeakSet();

function visit(user) {
visitedSet.add(user);
}

function isVisited(user) {
return visitedSet.has(user);
}

四、方法总结

Map 方法

方法/属性 说明
new Map([iterable]) 创建 map
map.set(key, value) 存储键值对,返回 map
map.get(key) 获取值,不存在返回 undefined
map.has(key) 检查键是否存在
map.delete(key) 删除指定键
map.clear() 清空 map
map.size 元素个数
map.keys() 返回键的可迭代对象
map.values() 返回值的可迭代对象
map.entries() 返回 [key, value] 的可迭代对象
map.forEach(fn) 遍历

Set 方法

方法/属性 说明
new Set([iterable]) 创建 set
set.add(value) 添加值,返回 set
set.has(value) 检查值是否存在
set.delete(value) 删除值
set.clear() 清空 set
set.size 元素个数
set.keys() 返回值的可迭代对象
set.values() 同 keys()
set.entries() 返回 [value, value] 的可迭代对象
set.forEach(fn) 遍历

五、实用示例

统计词频

1
2
3
4
5
6
7
8
9
10
11
12
13
function wordCount(str) {
let map = new Map();
let words = str.toLowerCase().match(/\w+/g) || [];

for (let word of words) {
map.set(word, (map.get(word) || 0) + 1);
}

return map;
}

let counts = wordCount('Hello world hello');
console.log(counts); // Map(2) { 'hello' => 2, 'world' => 1 }

过滤数组中的唯一元素

1
2
3
4
5
6
function unique(arr) {
return Array.from(new Set(arr));
}

let values = ['Hare', 'Krishna', 'Hare', 'Krishna', 'Krishna'];
console.log(unique(values)); // ['Hare', 'Krishna']

过滤字谜(anagrams)

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

for (let word of arr) {
let sorted = word.toLowerCase().split('').sort().join('');
map.set(sorted, word);
}

return Array.from(map.values());
}

let arr = ['nap', 'teachers', 'cheaters', 'PAN', 'ear', 'era', 'hectares'];
console.log(aclean(arr)); // ['PAN', 'hectares', 'era']

Map.keys() 转数组

1
2
3
4
5
6
7
8
9
10
11
let map = new Map([
['name', 'John'],
['age', 30]
]);

// map.keys() 返回可迭代对象,不是数组
let keys = Array.from(map.keys());
// 或
let keys2 = [...map.keys()];

keys.push('more'); // 现在可以使用数组方法了

参考资料