JavaScript Map
外观
JavaScript Map 是ES6引入的一种新型集合类型,用于存储键值对。与普通对象不同,Map允许使用任何数据类型(包括对象、函数等)作为键,并保留了元素的插入顺序。本指南将详细介绍Map的特性、用法及实际应用场景。
概述[编辑 | 编辑源代码]
Map是一种可迭代的键值对集合,具有以下核心特性:
- 任意类型的键(对象、原始值等)
- 保持插入顺序
- 通过
size
属性获取元素数量 - 高性能的查找操作(平均O(1)时间复杂度)
与Object的区别[编辑 | 编辑源代码]
特性 | Map | Object |
---|---|---|
任意值 | 仅字符串或Symbol | ||
是 | 否(ES6后普通属性按创建顺序) | ||
size 属性 | 手动计算
| ||
无原型链继承 | 继承Object.prototype |
基本用法[编辑 | 编辑源代码]
创建Map[编辑 | 编辑源代码]
// 空Map
const emptyMap = new Map();
// 从二维数组初始化
const map = new Map([
['name', 'Alice'],
[1, '数字键'],
[true, '布尔键']
]);
基本操作[编辑 | 编辑源代码]
const map = new Map();
// 添加元素
map.set('age', 30);
map.set({ id: 1 }, '对象键');
// 获取元素
console.log(map.get('age')); // 输出: 30
// 检查存在
console.log(map.has('age')); // 输出: true
// 删除元素
map.delete('age');
// 清空Map
map.clear();
// 获取大小
console.log(map.size); // 输出: 0
迭代方法[编辑 | 编辑源代码]
Map提供三种迭代器方法:
const map = new Map([
['a', 1],
['b', 2]
]);
// 1. keys()
for (const key of map.keys()) {
console.log(key); // 输出: 'a', 'b'
}
// 2. values()
for (const value of map.values()) {
console.log(value); // 输出: 1, 2
}
// 3. entries() (默认迭代方式)
for (const [key, value] of map) {
console.log(`${key}: ${value}`); // 输出: 'a: 1', 'b: 2'
}
// forEach方法
map.forEach((value, key) => {
console.log(`${key} => ${value}`);
});
性能特点[编辑 | 编辑源代码]
当需要频繁增删键值对时,Map的性能通常优于普通对象。这是因为:
- Map使用专门的哈希表实现
- 不需要处理原型链查找
- 内存分配更高效
时间复杂度分析[编辑 | 编辑源代码]
操作 | 平均复杂度 | 备注 |
---|---|---|
set() |
O(1) | |
get() |
O(1) | |
has() |
O(1) | |
delete() |
O(1) |
实际应用案例[编辑 | 编辑源代码]
案例1:用户权限系统[编辑 | 编辑源代码]
const userPermissions = new Map();
// 添加权限
userPermissions.set('user123', ['read', 'write']);
userPermissions.set('admin', ['read', 'write', 'delete']);
// 检查权限
function checkPermission(userId, permission) {
return userPermissions.get(userId)?.includes(permission) || false;
}
console.log(checkPermission('admin', 'delete')); // true
console.log(checkPermission('user123', 'delete')); // false
案例2:DOM节点元数据[编辑 | 编辑源代码]
const nodeMetadata = new Map();
// 关联DOM节点与数据
const button = document.querySelector('button');
nodeMetadata.set(button, { clicks: 0 });
// 事件监听器更新数据
button.addEventListener('click', () => {
const data = nodeMetadata.get(button);
data.clicks++;
console.log(`按钮点击次数: ${data.clicks}`);
});
案例3:缓存系统[编辑 | 编辑源代码]
class SimpleCache {
constructor() {
this.cache = new Map();
}
set(key, value, ttl = 1000) {
const expires = Date.now() + ttl;
this.cache.set(key, { value, expires });
}
get(key) {
const item = this.cache.get(key);
if (!item || Date.now() > item.expires) {
this.cache.delete(key);
return null;
}
return item.value;
}
}
高级特性[编辑 | 编辑源代码]
对象键的引用[编辑 | 编辑源代码]
Map使用严格相等(===)来比较键,但特殊处理了NaN:
const map = new Map();
const objKey = {};
map.set(objKey, '对象值');
map.set(NaN, '非数值');
console.log(map.get({})); // undefined (不同对象)
console.log(map.get(objKey)); // "对象值"
console.log(map.get(NaN)); // "非数值" (NaN === NaN 为false,但Map特殊处理)
Map与JSON转换[编辑 | 编辑源代码]
Map需要特殊处理才能与JSON互转:
const map = new Map([['a', 1], ['b', 2]]);
// Map转JSON
const jsonStr = JSON.stringify(Array.from(map));
console.log(jsonStr); // '[["a",1],["b",2]]'
// JSON转Map
const newMap = new Map(JSON.parse(jsonStr));
可视化表示[编辑 | 编辑源代码]
数学表示[编辑 | 编辑源代码]
Map可以形式化表示为: 其中:
- 是唯一键
- 是对应值
- 所有操作的时间复杂度为(平均情况)
最佳实践[编辑 | 编辑源代码]
- 当需要频繁增删键值对时优先使用Map而非Object
- 使用对象作为键时,确保保留对象引用
- 对大量数据考虑Map的内存效率优势
- 需要JSON序列化时提前规划转换方式
浏览器兼容性[编辑 | 编辑源代码]
Map在以下环境中完全支持:
- 所有现代浏览器(Chrome、Firefox、Safari、Edge)
- Node.js所有版本
- ES6兼容环境
对于旧环境,需要通过Babel等工具转译或使用polyfill。