Map 和 Set 是 ES6 独立内存结构的原生集合类型,需规范键值类型、初始化方式、读写操作及遍历方法,并配合 TypeScript 或 JSDoc 显式标注类型以避免隐性 bug。

Map 和 Set 是 ES6 原生集合类型,不是语法糖,也不是对象模拟——它们有独立内存结构、明确行为契约,用错场景或忽略细节容易埋下隐性 bug。团队协作中,统一编码规范能显著减少误用、提升可读性与可维护性。
键类型与初始化:明确意图,避免隐式转换
Map 的键支持任意类型(对象、函数、NaN、Symbol 等),但必须主动设计,不能依赖“碰巧可用”。Set 同理,值的唯一性由 SameValueZero(≈===,但 NaN === NaN)判定,而非字符串化。
- 禁止用字面量对象 {} 或数组 [] 直接当 Map 键来“测试是否能用”,而应提前声明变量并复用;否则两个
{id: 1}视为不同键,逻辑断裂 - 初始化优先使用二维数组字面量,语义清晰:
new Map([['id', 101], ['name', 'Alice']]);避免先new Map()再链式.set().set(),除非动态构建逻辑强 - Set 初始化时若含对象,需注明“按引用去重”:
// userIds 是 Set<string>,自动去重;userObjs 是 Set,每个对象独立存入</string>
读写操作:严格区分接口,禁用点号/方括号访问
Map 不是普通对象,不继承 Object.prototype,也不支持 map.key 或 map['key'] 访问。Set 更无“键”概念,只认 add/has/delete。
- 取值必须用
map.get(key),不可写作map.key或map[key];后者在 map 为空时可能意外返回 undefined,但一旦原型被污染(如有人给 Object.prototype 添加了 key),结果不可控 - 判断存在性统一用
map.has(key)和set.has(value),禁用map.get(key) !== undefined或arr.includes(x)替代 Set 查找 - 新增/更新一律用
map.set(key, value),不要尝试map[key] = value—— 这会把键转为字符串并挂到 Map 实例上,破坏数据一致性
遍历与转换:尊重迭代器本质,不强行转数组除非必要
Map 和 Set 都实现了 [Symbol.iterator],但返回结构不同:Map 默认迭代出 [key, value] 数组,Set 迭代出单个值。直接展开或 JSON 序列化会丢失数据。
- 遍历时优先用
for (const [k, v] of map)或for (const item of set);避免先Array.from(map)再.forEach,徒增内存开销 - 需要转数组时,明确目的:
Array.from(map.keys())获取键列表,[...set]获取去重后值数组;不要写JSON.stringify(map)——结果恒为"{}" - 批量处理推荐
map.forEach((v, k) => {...}),注意参数顺序是(value, key),与数组forEach的(item, index)不同,团队可加 ESLint 规则校验
类型与边界:配合 TypeScript 或 JSDoc 显式标注
JS 动态特性放大了 Map/Set 的误用风险。大型项目务必通过类型系统约束键值类型和使用方式。
- TypeScript 中声明应具体:
const userCache = new Map<string user>()</string>、const visitedIds = new Set<number>()</number>;避免泛型留空Map<any any></any> - 纯 JS 项目至少用 JSDoc 注释关键集合:
/** @type {Map<string string active: boolean>} */ const statusMap = new Map();</string> - 清空操作统一用
.clear(),禁止map = new Map()或set = new Set()赋值替换——若其他模块仍持有原引用,状态将不同步


















