Map 对象是个很特别的存在,它是一种键值对的集合。在 Map 中,每个键都是独一无二的,不能重复。这种设计确保了每个键值对在 Map 中都有唯一的位置。
当我们使用 for...of
循环遍历 Map 时,每次迭代都会返回一个形式为 [key, value]
的数组,并且这些键值对的顺序是按照它们被插入时的顺序进行的。这就意味着,当你用 set()
方法添加键值对时,Map 会记住它们插入的顺序,之后的迭代也是基于这个顺序的。
按照规范,Map 的实现要求“平均访问时间与集合中的元素数量呈次线性关系”。这听起来有点技术,但简单来说,就是 Map 的操作效率要比直接操作数组或者列表更高。因此,Map 可以内部实现为哈希表(查找操作是 O(1) 复杂度)、搜索树(查找操作是 O(log(N) 复杂度)等,只要能保证复杂度低于 O(N) 就可以。
至于键的相等性,Map 是基于“零值相等”算法来比较的。这与 JavaScript 中的 ===
运算符有点不同。特别是,Map 将 NaN
视为等同的,这一点与 NaN !== NaN
有所区别,但这是为了确保 Map 在处理特殊值时的一致性。
Object 和 Map 在很多情况下都可以用来存储键值对,但它们有一些显著的区别。以下是它们之间的详细对比,帮助你在适当的场景中选择合适的数据结构:
意外的键
toString
、hasOwnProperty
等)。如果不小心,这些默认键可能会与自己的键冲突。尽管可以使用 Object.create(null)
来避免这种情况,但这种做法并不常见。安全性
null
原型对象来缓解这个问题,但并不普遍。Symbol
。size
属性直接获得。Object.keys()
方法获得数组的长度,这个过程更为麻烦和低效。for...of
进行迭代。for...of
迭代。不过,可以使用 Object.keys()
、Object.entries()
或 for...in
来迭代对象的属性。JSON.stringify()
及其 replacer 参数和 JSON.parse()
及其 reviver 参数来构建自己的序列化和解析支持。JSON.stringify()
序列化对象为 JSON,并使用 JSON.parse()
解析 JSON 为对象。使用 Map 的情况:
当键的类型多样(不只是字符串或 Symbol
)。
当需要频繁添加和删除键值对。
当需要按照插入顺序迭代键值对。
当需要一个更加安全的数据结构,不用担心意外的原型污染。
使用 Object 的情况:
当键都是字符串或 Symbol
。
当需要使用 JSON 序列化和解析。
当需要一个简单的结构,不需要频繁的增删操作。
通过理解这些差异,你可以更好地选择合适的数据结构来满足具体需求。