Map
和 WeakMap
都是 JavaScript 中用于存储键值对的数据结构,但它们在设计目的、行为和使用场景上有一些重要的区别。
以下是 Map
和 WeakMap
之间的主要差异:
1. 键的类型
-
Map
:可以使用任何类型的值作为键,包括原始类型(如字符串、数字)和对象。let map = new Map(); let keyObj = { id: 1 }; let keyFunc = function() {}; map.set(1, 'number'); map.set('string', 'text'); map.set(keyObj, 'object'); map.set(keyFunc, 'function');
-
WeakMap
:只能使用对象作为键,不能使用原始类型(如字符串、数字等)作为键。let weakMap = new WeakMap(); let keyObj = { id: 1 }; weakMap.set(keyObj, 'object'); // OK weakMap.set(1, 'number'); // TypeError: Invalid value used in weak map
2. 垃圾回收机制
-
Map
:键值对中的键不会被自动垃圾回收,即使该键不再被其他地方引用,只要它存在于Map
中,它就不会被回收。 -
WeakMap
:键值对中的键是弱引用,这意味着如果一个对象只被WeakMap
引用而没有其他引用,那么这个对象可以被垃圾回收。这有助于防止内存泄漏,尤其是在处理大量临时对象时。
3. 迭代和操作
-
Map
:提供了丰富的 API 来进行遍历和操作,如keys()
、values()
、entries()
、forEach()
等方法,允许你轻松地遍历键、值或键值对。for (let [key, value] of map.entries()) { console.log(key + " = " + value); }
-
WeakMap
:没有提供迭代器,也没有keys()
、values()
、entries()
或forEach()
方法。因此,WeakMap
的内容无法直接遍历。这限制了WeakMap
的使用场景,但它也确保了对象的私有性,因为外部代码无法枚举WeakMap
的键。
4. 默认大小
-
Map
:可以通过size
属性获取当前Map
中存储的键值对数量。console.log(map.size); // 输出 Map 中的元素个数
-
WeakMap
:没有size
属性,也无法直接查询WeakMap
中有多少个键值对,因为它的键是弱引用的,可能会随时被垃圾回收。
5. 使用场景
-
Map
:适用于需要显式管理和遍历键值对的场景,比如缓存、字典查找、关联数组等。 -
WeakMap
:适用于需要与对象关联一些元数据而不阻止这些对象被垃圾回收的场景。例如,你可以使用WeakMap
来为 DOM 元素附加一些私有数据,当这些元素从文档中移除时,相关的WeakMap
条目也会自动被清理,避免内存泄漏。
6. 构造函数参数
-
Map
:可以接受一个可迭代对象(如数组)作为构造函数的参数来初始化Map
。let map = new Map([ [1, 'one'], ['two', 2], [{id: 3}, 'three'] ]);
-
WeakMap
:构造函数不接受任何参数,必须逐个添加键值对。let weakMap = new WeakMap(); weakMap.set({id: 1}, 'data');
总结来说,Map
更适合于通用的键值对存储需求,而 WeakMap
则更适合于需要与对象关联一些不会影响对象生命周期的数据的场景。选择使用哪一个取决于你的具体需求和应用场景。