1. Map
1.1 Map 的定义
Map
是一个包含键值对的集合,键和值都可以是任何类型。与普通的 JavaScript 对象不同,Map
的键可以是任何数据类型,而不仅仅是字符串或符号。
1.2 Map 的基本特性
- 任意类型的键:在
Map
中,键可以是任意类型的数据,包括对象、函数、甚至其他Map
实例。 - 有序性:
Map
会按照插入的顺序存储键值对,因此可以保证迭代的顺序。 - 键值对存储:
Map
存储的是键值对,键和值都可以是任何类型的对象。 - 大小:
Map
有一个size
属性,可以返回Map
中键值对的数量。 - 迭代:
Map
本身是可迭代的,支持forEach
循环和其他迭代方法(如for...of
)。 - 性能:与对象相比,
Map
在进行大量操作时,尤其是当涉及到非字符串类型的键时,通常性能更好。
1.3 Map 的常用方法
set(key, value)
:设置键值对,如果键已存在,则更新值。
js 代码解读复制代码 let map = new Map();
map.set('a', 1);
map.set('b', 2);
get(key)
:根据键获取值,如果键不存在返回undefined
。
js 代码解读复制代码 console.log(map.get('a')); // 1
console.log(map.get('c')); // undefined
has(key)
:判断Map
中是否包含指定的键。
js 代码解读复制代码 console.log(map.has('a')); // true
console.log(map.has('c')); // false
delete(key)
:删除指定键的键值对,返回true
或false
,表示删除成功或键不存在。
js 代码解读复制代码 map.delete('a'); // true
map.delete('c'); // false
clear()
:清空Map
中所有的键值对。
js 代码解读复制代码 map.clear(); // 清空 Map
size
:返回Map
中键值对的数量。
js 代码解读复制代码 console.log(map.size); // 2
- 迭代方法:
forEach(callback)
:对Map
中的每一个元素执行callback
函数。javascript map.forEach((value, key) => { console.log(key, value); });
keys()
:返回一个包含所有键的迭代器。values()
:返回一个包含所有值的迭代器。entries()
:返回一个包含所有键值对的迭代器。
1.4 Map 示例
js 代码解读复制代码let map = new Map();
// 添加键值对
map.set('name', 'Alice');
map.set(1, 'Number');
map.set(true, 'Boolean');
// 获取值
console.log(map.get('name')); // Alice
console.log(map.get(1)); // Number
// 判断键是否存在
console.log(map.has('name')); // true
console.log(map.has('age')); // false
// 删除键值对
map.delete(1); // 删除键为 1 的元素
console.log(map.size); // 2
// 遍历 Map
map.forEach((value, key) => {
console.log(key, value);
});
2. WeakMap
2.1 WeakMap 的定义
WeakMap
是一种类似于 Map
的数据结构,它存储的是键值对,但与 Map
不同的是,WeakMap
中的键必须是对象类型,且键是 弱引用。这意味着,WeakMap
不会阻止其键对象被垃圾回收。
2.2 WeakMap 的基本特性
- 键必须是对象:
WeakMap
的键只能是对象类型(包括数组、函数等),不能是基本数据类型(如字符串、数字等)。 - 弱引用:
WeakMap
中的键是弱引用,这意味着如果没有其他引用指向这个对象,垃圾回收器可以回收这些键值对。 - 不支持迭代:
WeakMap
不支持像Map
一样进行迭代,因为它的键是弱引用,可能会被垃圾回收,因此无法保证迭代顺序。 - 内存管理:
WeakMap
可以帮助管理内存,尤其是在存储大量数据时,可以避免不再使用的对象继续占用内存。
2.3 WeakMap 的常用方法
set(key, value)
:设置键值对,键必须是对象。
js 代码解读复制代码 let weakMap = new WeakMap();
let obj = {};
weakMap.set(obj, 'Some value');
get(key)
:根据键获取值,如果键不存在返回undefined
。
js 代码解读复制代码 console.log(weakMap.get(obj)); // 'Some value'
has(key)
:判断WeakMap
中是否包含指定的键。
js 代码解读复制代码 console.log(weakMap.has(obj)); // true
delete(key)
:删除指定键的键值对,返回true
或false
,表示删除成功或键不存在。
js 代码解读复制代码 weakMap.delete(obj); // true
2.4 WeakMap 示例
js 代码解读复制代码let weakMap = new WeakMap();
let obj1 = {};
let obj2 = {};
// 添加键值对
weakMap.set(obj1, 'Value 1');
weakMap.set(obj2, 'Value 2');
// 获取值
console.log(weakMap.get(obj1)); // 'Value 1'
// 删除键值对
weakMap.delete(obj1);
console.log(weakMap.has(obj1)); // false
// 垃圾回收:当 obj1 没有其他引用时,weakMap 中的键会自动被删除
3. Map 与 WeakMap 的区别
特性 | Map | WeakMap |
---|---|---|
键的类型 | 可以是任何类型(包括对象、函数等) | 键必须是对象类型 |
垃圾回收 | 键值对不会被垃圾回收机制影响 | 键是弱引用,可以被垃圾回收 |
支持迭代 | 支持 forEach、keys()、values()、entries() | 不支持迭代 |
大小 | 可以通过 .size 获取大小 | 不支持获取大小 |
内存管理 | 不适用于内存敏感应用 | 更适合内存管理,避免对象泄漏 |
- Map:适合需要频繁插入、删除和访问的场景,支持任意类型的键,支持迭代,且键值对在内存中常驻,直到被显式删除。
- WeakMap:适合需要存储对象的场景,尤其是在需要避免对象因被
Map
引用而无法垃圾回收的情况。由于键是弱引用,WeakMap
有助于内存管理,但不支持迭代,也没有size
属性。
4. Set
Set
是 ES6 引入的一种集合数据结构,用于存储一组唯一值,没有重复元素。
4.1 Set 的基本特点
- 唯一性:
Set
中的元素是唯一的,不能重复。如果你尝试插入重复的元素,它会自动忽略。 - 顺序性:
Set
保留插入元素的顺序。你插入的顺序就是遍历时的顺序。 - 可迭代:
Set
是可迭代的,支持for...of
和其他迭代方法进行遍历。 - 类型:
Set
可以存储任何类型的值,包括原始类型(如数字、字符串)和对象类型(如数组、对象)。 - 无索引访问:不像数组,
Set
不支持通过索引访问元素。
4.2 常用方法
add(value)
:向Set
中添加一个元素。如果该元素已存在,Set
会忽略它。
js 代码解读复制代码 let set = new Set();
set.add(1);
set.add(2);
set.add(1); // 不会添加,重复的元素会被忽略
console.log(set); // Set { 1, 2 }
has(value)
:检查Set
中是否包含指定的元素。
js 代码解读复制代码 console.log(set.has(1)); // true
console.log(set.has(3)); // false
delete(value)
:删除Set
中的指定元素。
js 代码解读复制代码 set.delete(1);
console.log(set); // Set { 2 }
clear()
:清空Set
中的所有元素。
js 代码解读复制代码 set.clear();
console.log(set); // Set {}
size
:返回Set
中元素的数量。
js 代码解读复制代码 console.log(set.size); // 2
forEach(callback)
:对Set
中的每个元素执行一个回调函数。
js 代码解读复制代码 set.forEach(value => console.log(value)); // 输出 1 和 2
values()
:返回一个包含Set
中所有值的迭代器对象。
js 代码解读复制代码 let iterator = set.values();
console.log(iterator.next().value); // 1
4.3 Set 的使用场景
- 去重:
Set
最常见的用途之一是去重。通过将一个数组转换为Set
,我们可以轻松去除其中的重复元素。
js 代码解读复制代码 let arr = [1, 2, 2, 3, 4, 4, 5];
let uniqueArr = [...new Set(arr)];
console.log(uniqueArr); // [1, 2, 3, 4, 5]
- 集合运算:
Set
可以很方便地进行集合的交集、并集和差集等运算。- 并集:可以通过将两个
Set
合并来实现并集。 - 交集:通过过滤操作来获取交集。
- 差集:通过过滤操作来获取差集。
- 并集:可以通过将两个
5. WeakSet
WeakSet
是 Set
的一个变种,主要用于存储对象的引用,并且这些引用是弱引用,意味着当没有其他地方引用这些对象时,它们会被自动清除。
5.1 WeakSet 的基本特点
- 只能存储对象:
WeakSet
只能存储对象,不能存储原始数据类型(如数字、字符串、布尔值等)。 - 弱引用:
WeakSet
中的元素是弱引用的。如果没有其他引用指向这些对象,它们会在垃圾回收时自动删除。 - 不支持遍历:
WeakSet
没有提供迭代器方法,如forEach()
、values()
、keys()
等,因此不能像Set
一样遍历元素。 - 没有
clear()
方法:由于元素是弱引用的,WeakSet
不需要清除操作,它们会在没有其他引用时被垃圾回收。
5.2 常用方法
add(value)
:向WeakSet
中添加一个对象。元素必须是对象类型。
ini 代码解读复制代码 let ws = new WeakSet();
let obj = {};
ws.add(obj);
has(value)
:检查WeakSet
中是否包含指定的对象。
arduino 代码解读复制代码 console.log(ws.has(obj)); // true
delete(value)
:删除WeakSet
中的指定对象。
scss 代码解读复制代码 ws.delete(obj);
console.log(ws.has(obj)); // false
5.3 WeakSet 的使用场景
内存管理:WeakSet
在内存管理中非常有用,因为它允许对象在没有其他引用时被垃圾回收。它非常适合用于存储和跟踪需要在某个时刻“标记”的对象。
- 比如,你可以用
WeakSet
来标记某个对象是否已被处理,但不会阻止这些对象被垃圾回收。
ini 代码解读复制代码 let ws = new WeakSet();
let obj1 = {};
let obj2 = {};
ws.add(obj1);
console.log(ws.has(obj1)); // true
obj1 = null; // 释放 obj1 的引用
// obj1 被垃圾回收,不再出现在 WeakSet 中
- 避免内存泄漏:
WeakSet
很适合用来避免内存泄漏,特别是在事件监听或对象管理的场景中,防止对象无法被回收。
5.4 Set 和 WeakSet 的对比
特性 | Set | WeakSet |
---|---|---|
存储类型 | 可以存储任何类型(原始值或对象) | 只能存储对象 |
引用方式 | 强引用,元素不会被垃圾回收 | 弱引用,元素会在没有其他引用时被垃圾回收 |
迭代 | 支持迭代(forEach()、values() 等) | 不支持迭代操作(无法使用 forEach() 等) |
内存管理 | 元素始终存在,直到手动删除 | 自动释放内存,当没有其他引用时会被垃圾回收 |
应用场景 | 去重、集合运算、缓存、处理唯一值等 | 对象标记、内存管理、避免内存泄漏 |
- 支持的数据类型:
Set
可以存储任何类型的值,而WeakSet
只能存储对象。 - 垃圾回收:
Set
中的元素是强引用,不会被垃圾回收,而WeakSet
中的元素是弱引用,能在没有其他引用时被自动回收。 - 是否支持遍历:
Set
支持遍历,而WeakSet
不支持遍历。这是因为WeakSet
的元素是弱引用,不能保证它们会一直存在。 - 内存管理:
WeakSet
用于管理对象的生命周期,适合用于标记和追踪对象而不会影响它们的垃圾回收。
5.5 使用选择
- 使用
Set
当你需要存储任意类型的唯一值时,并且需要对这些值进行迭代和其他集合操作。 - 使用
WeakSet
当你需要存储对象的弱引用,并且不需要对这些对象进行迭代或操作时,特别是在避免内存泄漏的场景中。
6. Map、WeakMap、Set、WeakSet总结
特性/集合类型 | Map | WeakMap | Set | WeakSet |
---|---|---|---|---|
存储数据 | 键值对(key-value) | 键值对(key-value),但键是弱引用 | 唯一值集合(无重复元素) | 唯一值集合(无重复元素),且值是对象 |
键类型 | 可以是任意数据类型(原始值、对象、函数等) | 键必须是对象(弱引用) | 可以是任意数据类型(原始值、对象、函数等) | 只能是对象(弱引用) |
值类型 | 可以是任意数据类型(原始值、对象、函数等) | 值可以是任意数据类型(原始值、对象、函数等) | 可以是任意数据类型(原始值、对象、函数等) | 可以是任意数据类型,但元素必须是对象 |
垃圾回收 | 键和值都不会被垃圾回收机制自动清除 | 键是弱引用,只有当对象没有其他引用时才会被回收 | 不涉及垃圾回收,值保持直到手动删除 | 键是弱引用,且只有对象作为值,当对象没有其他引用时,会被垃圾回收 |
大小 | 可通过 size 获取集合大小 | 无 size 属性,不能直接获取集合大小 | 可通过 size 获取集合大小 | 无 size 属性,不能直接获取集合大小 |
遍历支持 | 支持遍历(forEach , for-of , keys() , values() , entries() ) | 不支持遍历 | 支持遍历(forEach , for-of , values() , keys() , entries() ) | 不支持遍历 |
更新操作 | 支持更新键值对(set() ,delete() ,clear() ) | 不支持更新键值对,只能 set() 新增键值对或 delete() 删除 | 支持添加、删除元素(add() ,delete() ,clear() ) | 支持添加、删除元素(add() ,delete() ,clear() ) |
性能 | 查找性能优于普通对象,适合大量数据存储和快速查找 | 由于弱引用机制,适合处理需要垃圾回收的对象数据 | 查找性能较好,适用于去重操作 | 适合用于存储对象引用,避免内存泄漏 |
用途场景 | 存储键值对,适合需要频繁更新和查找的场景 | 存储与对象相关的元数据,防止内存泄漏 | 存储唯一值,去重操作,高效查找 | 追踪对象引用,防止内存泄漏 |
6.1 对比
相同点:
Map
和WeakMap
都是存储键值对的数据结构,而Set
和WeakSet
存储的是唯一的值。Set
和WeakSet
不允许元素重复;Map
和WeakMap
中的键是唯一的。WeakMap
和WeakSet
都是基于弱引用来处理数据,因此可以避免内存泄漏的问题。- 所有四种数据结构都支持
delete()
方法来删除特定元素。
不同点:
- 键/值:
Map
和WeakMap
都存储键值对,Set
和WeakSet
只存储值。 - 数据类型限制:
WeakMap
和WeakSet
的键/值必须是对象,而Map
和Set
的元素可以是任何类型。 - 垃圾回收:
WeakMap
和WeakSet
利用弱引用机制,垃圾回收时会自动清理不再被引用的对象;而Map
和Set
没有这种机制。 - 遍历支持:
Map
和Set
都支持遍历操作(可以使用forEach
或者for-of
),但WeakMap
和WeakSet
由于使用弱引用,不支持遍历。
6.2 应用场景
Map
- 缓存:适合用于存储键值对,尤其是当键可以是复杂对象或函数时。
- 关联数组:需要根据特定键查找、更新或删除值时,
Map
是一个优秀的选择。 - 数据存储与查询:例如用户设置、存储对象与属性映射等场景。
WeakMap
- 防止内存泄漏:当需要关联对象时(例如将附加数据存储在 DOM 元素上),
WeakMap
可以避免内存泄漏,因为它会自动清理不再使用的键。 - 私有数据:可以通过
WeakMap
模拟私有属性,只能通过对象的引用访问。
Set
- 去重:使用
Set
可以方便地去除数组中的重复元素。 - 集合运算:
Set
可以用于表示数学中的集合,进行交集、并集和差集运算。 - 快速查找:当需要频繁检查某个值是否存在于集合中时,
Set
提供了高效的查找操作。
WeakSet
- 对象引用管理:用于管理对象引用,确保对象在不再使用时可以被垃圾回收。
- 避免内存泄漏:适用于跟踪 DOM 元素或其他对象的引用,并在对象不再被引用时自动清理内存。
- 资源回收:如 DOM 元素、事件监听器等,当对象不再使用时自动释放内存。
评论记录:
回复评论: