跳转到内容

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));

可视化表示[编辑 | 编辑源代码]

graph LR A[Map] --> B[键值对1] A --> C[键值对2] A --> D[...] B --> E[键] B --> F[值] C --> G[键] C --> H[值]

数学表示[编辑 | 编辑源代码]

Map可以形式化表示为: M={(k1,v1),(k2,v2),...,(kn,vn)} 其中:

  • ki 是唯一键
  • vi 是对应值
  • 所有操作的时间复杂度为O(1)(平均情况)

最佳实践[编辑 | 编辑源代码]

  • 当需要频繁增删键值对时优先使用Map而非Object
  • 使用对象作为键时,确保保留对象引用
  • 对大量数据考虑Map的内存效率优势
  • 需要JSON序列化时提前规划转换方式

浏览器兼容性[编辑 | 编辑源代码]

Map在以下环境中完全支持:

  • 所有现代浏览器(Chrome、Firefox、Safari、Edge)
  • Node.js所有版本
  • ES6兼容环境

对于旧环境,需要通过Babel等工具转译或使用polyfill。

参见[编辑 | 编辑源代码]