我刚刚发现了这个特点:

Map: Map对象是简单的键/值映射。

这让我很困惑。常规JavaScript对象是字典,那么Map与字典有什么不同呢?从概念上讲,它们是相同的(根据Stack Overflow的另一个问题)

文档也没有帮助:

Map对象是键/值对的集合,其中键和值都可以是任意的ECMAScript语言值。不同的键值只能出现在Map集合中的一个键/值对中。使用创建Map时选择的比较算法进行区分的不同键值。

Map对象可以按插入顺序迭代其元素。Map对象必须使用哈希表或其他机制来实现,这些机制提供的访问时间平均与集合中元素的数量呈次线性关系。本Map对象规范中使用的数据结构仅用于描述Map对象所需的可观察语义。它并不是一个可行的实现模型。

听起来还是像个物件,显然我错过了什么。

为什么JavaScript获得一个(受良好支持的)Map对象?它能做什么?


当前回答

除了按定义良好的顺序可迭代,以及能够使用任意值作为键(除了-0)之外,map还很有用,原因如下:

The spec enforces map operations to be sublinear on average. Any non-stupid implementation of object will use a hash table or similar, so property lookups will probably be constant on average. Then objects could be even faster than maps. But that is not required by the spec. Objects can have nasty unexpected behaviors. For example, let's say you didn't set any foo property to a newly created object obj, so you expect obj.foo to return undefined. But foo could be built-in property inherited from Object.prototype. Or you attempt to create obj.foo by using an assignment, but some setter in Object.prototype runs instead of storing your value. Maps prevent these kind of things. Well, unless some script messes up with Map.prototype. And Object.create(null) would work too, but then you lose the simple object initializer syntax.

其他回答

这两个技巧可以帮助你决定是使用Map还是Object:

当键在运行时之前未知时,以及当 所有键都是相同的类型,所有值都是相同的类型。 如果需要将原始值存储为键,则使用映射 因为object将每个键都视为字符串,要么是数字值, 布尔值或任何其他基本值。 当存在操作单个元素的逻辑时使用对象。

来源:键集合

除了上面提到的可用性差异,如果你对大型对象的性能差异更感兴趣,普通对象在chrome上设置、更新和删除大量数据时似乎要快2倍。

Experiment here: https://perf.link/#eyJpZCI6ImNncmtpcGdlMzhsIiwidGl0bGUiOiJGaW5kaW5nIG51bWJlcnMgaW4gYW4gYXJyYXkgb2YgMTAwMCIsImJlZm9yZSI6ImNvbnN0IGRhdGEgPSBbLi4uQXJyYXkoMTAwMDAwKS5rZXlzKCldXG4iLCJ0ZXN0cyI6W3sibmFtZSI6IkZpbmQgaXRlbSAxMDAiLCJjb2RlIjoiY29uc3Qgb2JqID0ge31cbmZvciAoY29uc3QgdiBvZiBkYXRhKSB7XG4gICBvYmpbdl0gPSB2XG59XG5cbmZvciAoY29uc3QgdiBvZiBkYXRhKSB7XG4gICBvYmpbdl0gPSB2ICsgMVxufVxuXG5mb3IgKGNvbnN0IHYgb2YgZGF0YSkge1xuICAgZGVsZXRlIG9ialt2XVxufSIsInJ1bnMiOlsxMTUsMTE1LDExNSwxMTUsMTE1LDExNSwxMDksMTE1LDEwOSwxMTUsMTE1LDExNSwxMDksMTE1LDExNSwxMTUsMTA5LDEwOSwxMDksMTE1LDEwOSwxMDksMTE1LDEwOSwxMTUsMTA5LDEwOSwxMTUsMTA5LDExNSwxMDksMTE1LDEwOSwxMDksMTA5LDEwOSwxMDksMTA5LDExNSwxMDksMTA5LDEwOSwxMDksMTE1LDEwOSwxMDksMTA5LDEwOSwxMDksMTE1LDEwOSwxMDksMTA5LDEwOSwxMDksMTA5LDEwOSwxMDksMTA5LDEwOSwxMDksMTA5LDEwOSwxMTUsMTA5LDEwOSwxMDksMTA5LDEwOSwxMDksMTA5LDEwOSwxMTUsMTE1LDEwOSwxMDksMTA5LDEwOSwxMDksMTA5LDEwOSwxMDksMTA5LDEwOSwxMDksMTA5LDEwOSwxMDksMTA5LDEwOSwxMDksMTA5LDEwOSwxMDksMTA5LDEwOSwxMDksMTA5LDEwOSwxMDldLCJvcHMiOjExMH0seyJuYW1lIjoiRmluZCBpdGVtIDIwMCIsImNvZGUiOiJjb25zdCBvYmogPSBuZXcgTWFwKClcbmZvciAoY29uc3QgdiBvZiBkYXRhKSB7XG4gICBvYmouc2V0KHYsIHYpXG59XG5cbmZvciAoY29uc3QgdiBvZiBkYXRhKSB7XG4gICBvYmouc2V0KHYsIHYgKyAxKVxufVxuXG5mb3IgKGNvbnN0IHYgb2YgZGF0YSkge1xuICAgb2JqLmRlbGV0ZSh2KVxufSIsInJ1bnMiOls2Myw1Nyw1Nyw2Myw1Nyw2Myw1Nyw1Nyw1Nyw2Myw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw2Myw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1Nyw1N10sIm9wcyI6NTd9XSwidXBkYXRlZCI6IjIwMjItMDYtMTVUMTg6MTQ6MjYuNjA2WiJ9

除了按定义良好的顺序可迭代,以及能够使用任意值作为键(除了-0)之外,map还很有用,原因如下:

The spec enforces map operations to be sublinear on average. Any non-stupid implementation of object will use a hash table or similar, so property lookups will probably be constant on average. Then objects could be even faster than maps. But that is not required by the spec. Objects can have nasty unexpected behaviors. For example, let's say you didn't set any foo property to a newly created object obj, so you expect obj.foo to return undefined. But foo could be built-in property inherited from Object.prototype. Or you attempt to create obj.foo by using an assignment, but some setter in Object.prototype runs instead of storing your value. Maps prevent these kind of things. Well, unless some script messes up with Map.prototype. And Object.create(null) would work too, but then you lose the simple object initializer syntax.

除了其他答案之外,我还发现map比对象操作起来更笨拙和冗长。

obj[key] += x
// vs.
map.set(map.get(key) + x)

这一点很重要,因为较短的代码读起来更快,表达更直接,并且更好地记在程序员的脑海里。

另一方面:因为set()返回的是映射,而不是值,所以不可能链式赋值。

foo = obj[key] = x;  // Does what you expect
foo = map.set(key, x)  // foo !== x; foo === map

调试地图也更加痛苦。下图中,你实际上看不到地图中的键是什么。你必须编写代码才能做到这一点。

对象可以被任何IDE计算:

这是我记住它的一个简单方法:KOI

Keys. Object key is strings or symbols. Map keys can also be numbers (1 and "1" are different), objects, NaN, etc. It uses === to distinguish between keys, with one exception NaN !== NaN but you can use NaN as a key. Order. The insertion order is remembered. So [...map] or [...map.keys()] has a particular order. Interface. Object: obj[key] or obj.a (in some language, [] and []= are really part of the interface). Map has get(), set(), has(), delete() etc. Note that you can use map[123], but that is using it as a plain JavaScript object.