
本文共 2918 字,大约阅读时间需要 9 分钟。
ES6 Map和WeakMap深入解析
1. Map的基本语法与使用方法
在JavaScript中,ES6引入了Map数据结构,它是一种键值对集合,与传统的对象有很大不同。Map的键不仅可以是字符串,还可以是任意类型的值。它提供了更灵活的数据存取方式,适用于复杂的数据管理需求。
Map的定义非常简单。以下是创建一个Map的基本语法:
let map = new Map();
如果你想初始化多个键值对,可以使用数组或其他可迭代对象:
let map = new Map([[1, 'one'], [2, 'two']]);
那么,这些键值对会被依次添加到Map中。如果你传递一个非可迭代对象,Map会直接忽略它。这也是一个需要注意的地方:null被当作undefined处理,可能会导致意想不到的结果。
添加、删除和查询数据
除了创建Map对象,实际应用中你还需要对Map进行插入、删除和查询操作。Map提供了多种方法来完成这些任务。
添加数据:
let keyObj = {}; // 一个空对象let keyFunc = function() {}; // 一个函数let keyString = 'a string'; // 一个字符串map.set(keyObj, 'object value'); // 添加键(keyObj)对应的值map.set(keyFunc, 'function value');map.set(keyString, 'string value');
删除数据:
map.delete(keyString);map.clear(); // 删除所有键值对
查询数据:
map.get(keyObj); // 返回键keyObj对应的值
统计数据及判断是否存在键值对:
console.log(map.size); // 输出当前键值对的总数console.log(map.has(keyObj)); // 返回true或false,判断键是否存在
Map的遍历方式
Map提供了多种方法来遍历其键值对。以下是一些常用方法:
keys()
:返回Map中所有键的可迭代对象values()
:返回Map中所有值的可迭代对象entries()
:返回键值对的数组形式,可迭代对象forEach()
:按照插入顺序对Map进行遍历for...of
:可以直接遍历Map中的键、值和键值对
示例代码:
for (let key of map.keys()) {console.log(key);}for (let value of map.values()) {console.log(value);}for (let [key, value] of map.entries()) {console.log(key, value);}map.forEach((value, key) => {console.log(value, key);});for (let [key, value] of map) {console.log(key, value);}
Map与Object的对比
虽然Object也能存储键值对,但Map有着明显的优势:
- 键的类型:Map中的键可以是任何类型,包括对象和函数;而Object的键只能是字符串或Symbol。
- 键的顺序:Map中的键值对是有序的,按照插入顺序存储;而Object的键值对存储顺序是无序的。
- 数据统计:Map提供了
size
属性,直接可以得到键值对的总数;而Object需要手动统计。 - 遍历方式:Map提供了更丰富的遍历方法,支持键、值、键值对的独立遍历和联合遍历。
- 性能:Map在频繁进行键值对的插入、删除和查询操作时,性能远优于传统的Object结构。
2. WeakMap的概念与应用
WeakMap是一种弱引用键的Map。它和Map的主要区别在于:
- WeakMap的键必须是对象(包括null);不能使用符号 <|null等其他类型作为键。
- WeakMap不会保留引用的对象。如果对象被垃圾回收机制回收,那么WeakMap中的相关键值对会被自动删除。
WeakMap的主要用途在于缓存那些可能会多次被请求但不需要长期保留的数据。在JavaScript中最常见的场景包括:
WeakMap的使用示例:
const cache = new WeakMap();function countOwnKeys(obj) {if (cache.has(obj)) {return [cache.get(obj), 'cached'];} else {const count = Object.keys(obj).length;cache.set(obj, count);return [count, 'computed'];}}let obj = {name: "kakuqo",age: 30};console.log(countOwnKeys(obj)); // [2, 'computed']console.log(countOwnKeys(obj)); // [2, 'cached']obj = null; // 当对象不再使用时,默认为null
在实际应用中,WeakMap可以帮助我们更好地管理内存,避免内存泄漏。例如,在类似于计数器的场景中,我们可以使用WeakMap临时存储计算结果,而不必担心内存的占用问题。
WeakMap的具体实现
WeakMap的构造方法非常类似Map。它可以接受一个数组作为构造参数,或者直接通过set方法逐个添加键值对。但需要注意的是,WeakMap的键只能是对象或null,不支持其他数据类型作为键。
const wm = new WeakMap();wm.set(1, 2);// TypeError: 1 is not an object! 这表明1不能作为WeakMap的键wm.set(Symbol(), 2);// TypeError: Invalid value used as weak map keywm.set(null, 2);// TypeError: Invalid value used as weak map key
如果你尝试使用非对象类型作为键,Map会抛出错误。只有理论上只能是对象(包括null)的键才能被接受。这也是WeakMap之所以名称的由来——弱引用键的Map。
总结一下,WeakMap适合场景包括:
- 缓存计算结果
- 管理私有属性
- 处理可能需要释放内存的键值对
在你的代码中,WeakMap可以帮助你更好地管理内存,避免高内存占用和内存泄漏问题。不知道你的项目中有没有类似需求的话,WeakMap是一个非常有用的工具。
通过以上了解,你应该对Map和WeakMap有了更清晰的认识。如果你想更深入的学习,可以额外研究它们的具体实现方式,还可以结合它们在实际项目中的应用场景进行实践练习。