在我们日常开发中 Object
使用到的频率出奇的高,而Map
和 Object
类似,都允许按 键-值(key-value)
对储数据,也可以对一个键进行删除和检测该键值是否存在。诚然在 ES2015 之前我们只能通过 Object
的形式来存储键值对的数据结构,但是现在已经是3202年了,是时候尝试用 Map
来代替 Object
作为 键值对数据结构的唯一选择了。
Map | Object | |
---|---|---|
额外的键 | Map 默认情况下不包含任何键值。只会包含显示插入的键 | 一个 Object 有一个原型,原型链上的键名可能和专家对象上设置的键名产生冲突 (tips: 可以使用 Object.create(null) 来创建一个没有原型的对象) |
键的类型 | 一个 Map 的键可以是任意值,包括函数、对象或任意基本类型 | 一个 Object 的键必须是一个 String 或者是 Symbol |
键的顺序 | Map 中的键是有序的。因此,在迭代的时候,一个Map 对象以插入的顺序返回键值 | 虽然 Object 的键目前是有序的,但并不总是这样,而且这个顺序是复杂的。因此,最好不要依赖属性的顺序。自 ECMAScript 2015 规范以来,对象的属性被定义为是有序的;ECMAScript 2020 则额外定义了继承属性的顺序。参见 OrdinaryOwnPropertyKeys 和 EnumerateObjectProperties 抽象规范说明。但是,请注意没有可以迭代对象所有属性的机制,每一种机制只包含了属性的不同子集。( for-in 仅包含了以字符串为键的属性;Object.keys 仅包含了对象自身的、可枚举的、以字符串为键的属性;Object.getOwnPropertyNames 包含了所有以字符串为键的属性,即使是不可枚举的;Object.getOwnPropertySymbols 与前者类似,但其包含的是以 Symbol 为键的属性,等等。) |
大小 | Map 的键值对个数可以轻易地通过 size 属性获取。 | Object 的键值对个数只能手动计算 |
迭代 | Map 是 可迭代的 的,所以可以直接被迭代。 | Object 没有实现 迭代协议,所以使用 JavaSctipt 的 for…of 表达式并不能直接迭代对象。tips: 对象可以实现迭代协议,或者你可以使用 Object.keys 或 Object.entries for…in 表达式允许你迭代一个对象的可枚举属性 |
性能 | 在频繁增删键值对的场景下表现更好。 | 在频繁添加和删除键值对的场景下未作出优化。 |
序列化和解析 | 没有元素的序列化和解析的支持。 (但是你可以使用携带 replacer 参数的 JSON.stringify() 创建一个自己的对 Map 的序列化和解析支持。 | 原生的由 Object 到 JSON 的序列化支持,使用 JSON.stringify() 。原生的由 JSON 到 Object 的解析支持,使用 JSON.parse() 。 |
设置对象属性
设置对象属性同样适用于 Map 对象,但容易造成困扰。
即,以下的代码能够正常运行(但不推荐):
const wrongMap = new Map();
wrongMap['bla'] = 'blaa';
wrongMap['bla2'] = 'blaaa2';
console.log(wrongMap); // Map { bla: 'blaa', bla2: 'blaaa2' }
但这种设置属性的方式不会改变 Map 的数据结构。它使用的是通用对象的特性。'bla'
的值未被存储在 Map 中,无法被查询到。其他的对这一数据的操作也会失败:
wrongMap.has('bla') // false
wrongMap.delete('bla') // false
console.log(wrongMap) // Map { bla: 'blaa', bla2: 'blaaa2' }
正确的存储数据到 Map 中的方式是使用 set(key, value)
方法。
const contacts = new Map()
contacts.set('Jessie', {phone: "213-555-1234", address: "123 N 1st Ave"})
contacts.has('Jessie') // true
contacts.get('Hilary') // undefined
contacts.set('Hilary', {phone: "617-555-4321", address: "321 S 2nd St"})
contacts.get('Jessie') // {phone: "213-555-1234", address: "123 N 1st Ave"}
contacts.delete('Raymond') // false
contacts.delete('Jessie') // true
console.log(contacts.size) // 1