link:Use Maps More and Objects Less
如果在
JavaScript
中使用对象来存储任意键值对,并且频繁的添加和删除键值时,应该考虑使用Map
而不是Object
首先是性能问题,对于 Object
的删除操作性能很差,而Map
对则进行了优化,在某些情况下可以更快,同时 MDN 本身也指出,与对象相比,Map
专门针对频繁添加和删除进行了优化:JavaScript 标准内置对象 > Map,MDN 上关于 Object
和 Map
的对比
Map | Object | |
---|---|---|
意外的键 | Map 默认情况不包含任何键,只包含显示插入的键 | Object 自身带有一个原型,原型链上的键名可能和你自己在对象上设置的键名冲突 |
键的类型 | 一个 Map 的键可以是任意值,包括函数、对象或任意基本类型 | Object 的键必须是一个 String 或者 Symbol |
键的顺序 | Map 中的键是有序的,当迭代的时候,一个 Map 对象以插入的顺序返回键值 | 虽然 Object 的键目前是有序的,但并不总是这样,而且这个顺序是复杂的,最好不要依赖属性的顺序 |
Size | Map 的键值对个数可以轻易的通过 size 属性获取 | Object 的键值对个数只能手动计算 |
迭代 | Map 是可迭代的,所以可以直接被迭代 | Object 没有实现迭代协议,所以使用 JS 的 for…of 表达式并不能直接迭代对象,可以使用 Object.keys 或者 Obkect.entries/或者使用 for..in 表达式迭代一个对象的可枚举属性 |
性能 | 在频繁增删键值对的场景下表现很好 | 在频繁添加和删除键值对的场景下未做优化 |
序列化和解析 | 没有元素的序列化和解析的支持 | JSON.stringify() 和 JSON.parse() |
除了性能问题之外,Map
还解决了对象存在的其他几个问题:
内置键问题
当你创建一个空Object
的时候,虽然它是空的,但是它其实已经有值了,Object
原型默认带有一些内置键
在使用 Object
的时候,很有可能你稍微不注意,就会造成一些严重的错误。
迭代问题
在JavaScript
中遍历一个对象,充满了陷阱,例如使用 for..in
安全一点的做法
如果担心 hasOwnProperty
被不小心覆盖,还可以更安全
如果觉得写起来繁琐,还可以使用新添加的 Object.hasOwn
你甚至可以放弃for
循环,只使用Object.keys()
和 forEach
但是对于Map
来说,根本不会出现这样的问题,你可以直接迭代
或者直接迭代Map
的键和值
甚至可以通过Object.entries
方法将Object
转换为一个Map
后再迭代
键的顺序
在Map
中建的顺序是按照设置时的顺序排列的,你可以很安全的从Map
中解构键
复制
Object
很容易复制,使用对象扩展符或者Object.assign({}, myObject)
但Map
同样容易复制
这是因为Map
的构造函数接受了[key, value]
元祖的可迭代对象,且Map
是可迭代的,类似的,也可以做Map
的深度拷贝:
structuredClone()
全局的方法,使用结构化克隆算法将给定的值进行深拷贝,该方法还支持把原始值中的可转移对象转移到新对象,而不是把属性引用拷贝过去。可转移动向与原始对象分别并附加到新对象,他们不可以在原始对象中访问被访问到
Map和 Object 的相互转换
Map
转Object
Object
转Map
使用元祖来构造 Map
或者像创建对象一样创建 Map
键可以是任意值
在Map
中,键的类型不一定必须是String
或者 Symbol
,它可以是任意值
这样又很多好处,但是同时也带来了新的问题
为了避免这种问题,就出现了WeekMap
类型,它是一个弱引用类型的数据结构,很好的解决了上面说的内存泄漏问题
WeekMap/WeekSet
WeekMap
持有对象的弱引用 ,当其他对象被删除时,对象将自动被垃圾收回器收集并从这个弱引用中删除。
除了Map
之外,还有它的好兄弟Set
,它提供了一种更好的方式来创建一个唯一的元素列表,在某些情况下,集合可以产生必使用数组的同等操作更好的性能。
同样,Set
也有它所对应的弱引用类型WeekSet
, 可以帮助我们避免Set
内存泄漏.
序列化和解析
Set
和Map
本身不支持序列化和反序列化,但是我们可以使用JSON.stringify
及它的第二个参数将map
和set
转换为对象和数组进行序列化
相反我们同样可以使用JSON.parse
及它的第二个参数进行反序列化
什么时候应该使用 Map/Set
对于具有良好的键集的结构化对象,例如每个 event
都应该有一个标题和一个日期,这通常需要一个对象
当你需要拥有任意数量的键,并且可能需要频繁的添加和删除时,这时候需要考虑使用Map
以获得更好的性能和使用体验
当需要一个不包含重复元素的数组时,并且数组元素的顺序无关紧要时,考虑使用Set