来源:https://zhuanlan.zhihu.com/p/358378689

背景

在 JavaScript 中,除了最基础的 Object 是该格式外,ES6 新增的 Map 也同样是键值对格式。它们的用法在很多时候都十分接近。不知道有没有人和我一样纠结过该选择哪个去使用呢?在本菜最近的项目中,我又遇到了这样的烦恼,索性一不做二不休,去对比一下究竟该使用哪一个。

用法的区别

  • key 类型

Object,只能用字符串、数字或者Symbol等简单数据类型当作键。
Map,它可以是任何类型。(包括 Date,Map,或者自定义对象)

  • 插入顺序

Map 中的元素会保持其插入时的顺序;
Object 则不会完全保持插入时的顺序,而是根据如下规则进行排序:

  • 非负整数会最先被列出,排序是从小到大的数字顺序
  • 然后所有字符串,负整数,浮点数会被列出,顺序是根据插入的顺序
  • 最后才会列出 Symbol,Symbol 也是根据插入的顺序进行排序的
  • 读取长度

Object,Object.keys(obj).length
Map,Map.size

  • 可迭代

Object 默认是不可迭代的,只能通过 for in 循环来访问
Map 是可迭代对象,其中的键值对是可以通过 for of 循环或 .foreach() 方法来迭代的

  • 新增同名键

Object 新增同名键时,会被覆盖
Map 新增同名键时,key是引用类型话不会被覆盖(key保存的是内存地址)

性能对比

  • 创建 Object 和 Map 时的表现

创建 Object 的速度会快于 Map,空的 Object 会比空的 Map 占用更少的内存

  • 新增元素时的性能

新建元素时,Map 的速度会快于 Object,通过对比我们可以发现,在拥有一定数量的元素时, Object 会比 Map 占用多了约 78% 的内存。所以,在需要进行很多新增操作,且需要储存许多数据的时候,使用 Map 会更高效。

  • 读取元素时的性能

Object 略占优势,但总体差别不大。

  • 删除元素时的性能

Map 略占优势,但总体差别不大。

  • 特殊情况

还记得我们在前面提到的 Object 中键的排序吗?我们提到了其中的非负整数会被最先列出。其实对于非负整数作为键的值和其余类型作为键的值来说,v8 是会对它们进行区别对待的。负整数作为键的部分会被当成数组对待,即非负整数具有一定的连续性时,会被当成快数组,而过于稀疏时会被当成慢数组。对于快数组,它拥有连续的内存,所以在进行读写时会更快,且占用更少的内存。
在键为连续非负整数时,Object 不仅平均速度更快了,其占用的内存也大大减少了。

总结

通过对比我们可以发现,Map 和 Object 各有千秋,对于不同的情况下,我们应当作出不同的选择。所以我总结了一下我认为使用 Map 和 Object 更为合适的时机。

使用 Map:

  • 储存的键不是字符串/数字/或者 Symbol 时,选择 Map,因为 Object 并不支持
  • 储存大量的数据时,选择 Map,因为它占用的内存更小
  • 需要进行许多新增/删除元素的操作时,选择 Map,因为速度更快
  • 需要保持插入时的顺序的话,选择 Map,因为 Object 会改变排序
  • 需要迭代/遍历的话,选择 Map,因为它默认是可迭代对象,迭代更为便捷

使用 Object:

  • 只是简单的数据结构时,选择 Object,因为它在数据少的时候占用内存更少,且新建时更为高效
  • 需要用到 JSON 进行文件传输时,选择 Object,因为 JSON 不默认支持 Map
  • 需要对多个键值进行运算时,选择 Object,因为句法更为简洁
  • 需要覆盖原型上的键时,选择 Object

虽然 Map 在很多情况下会比 Object 更为高效,不过 Object 永远是 JS 中最基本的引用类型,它的作用也不仅仅是为了储存键值对。