我刚刚发现了这个特点:

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

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

文档也没有帮助:

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

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

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

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


当前回答

对象的行为类似于字典,因为JavaScript是动态类型的,允许您随时添加或删除属性。

但是Map()更好,因为它:

提供get、set、has和delete方法。 接受任何类型的键,而不仅仅是字符串。 提供一个易于for-of使用的迭代器,并维护结果的顺序。 在迭代或复制过程中不会出现原型和其他属性的边缘情况。 支持数百万项。 非常快。

如果需要字典,则使用Map()。

但是,如果您只使用基于字符串的键,并且需要最大的读取性能,那么对象可能是更好的选择。这是因为JavaScript引擎在后台将对象编译为c++类,并且属性的访问路径比Map().get()的函数调用要快得多。

这些类也被缓存,所以创建一个具有完全相同属性的新对象意味着引擎将重用一个现有的后台类。添加或删除属性会导致类的形状改变,并重新编译支持类,这就是为什么将一个对象用作添加和删除大量内容的字典会非常慢,但是在不更改对象的情况下读取现有键会非常快。

因此,如果您有一个写一次读的繁重的工作负载和字符串键,那么您可以使用对象作为高性能的字典,但对于其他所有事情使用Map()。

其他回答

Map的一个方面在这里没有给予太多关注,那就是查找。根据规格:

Map对象必须使用哈希表或其他方式实现 平均而言,提供次线性访问时间的机制 关于集合中元素的数量。使用的数据结构 在此Map对象规范中仅用于描述 需要Map对象的可观察语义。这并非有意为之 一个可行的实现模型。

对于具有大量项并需要项查找的集合,这将极大地提高性能。

TL;DR -没有指定对象查找,因此它可以按照对象中元素数量的顺序,即O(n)。映射查找必须使用哈希表或类似的方法,因此无论映射大小如何,即O(1),映射查找都是相同的。

TL;DR:你不能在对象中使用键' length'来存储值,但在Map中可以。

对于我来说,不使用object而使用Map的实际考虑是,从实际的角度来看,object的键不是字符串或数字,而是字符串或数字的子集。具体来说,使用与原型的属性名或众所周知的属性冲突的键可能会导致问题。例如,存储具有键长度的值可能会使使用length键来确定给定对象是否为数组的代码混淆。

根据Mozilla

JavaScript中对象与地图的简短示例。

Object-遵循与map相同的概念,即使用键值对存储数据。但也有细微的差异,使得地图在某些情况下表现更好。

Map-是一种数据结构,有助于以对的形式存储数据。这一对由一个唯一键和映射到该键的值组成。这有助于防止口是心非。

关键的不同点

Map是对象的实例,反之则不然。

var map = new map (); var obj = new Object(); console.log(obj instanceof Map);/ /错误 console.log(映射实例对象);/ /正确的

在Object中,键字段的数据类型被限制为整数、字符串和符号。而在Map中,键字段可以是任何数据类型(整数、数组、对象)

var map = new map();//空 map.set (1, ' 1 '); 地图。(' 1 ', 1); 地图。set('{}', {name:'Hello, World!'}); map.set (12.3, 12.3) map.set([12]、[12345]) (let [key,value] of map.entries()) console.log(键+“-”+值)

在Map中,元素的原始顺序被保留。这在对象的情况下是不正确的。

让obj ={ 1: ' 1 ', “一”:1、 '{}': {name:'Hello world'}, 12.3:12.3, [12]: [100] } console.log (obj)

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

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

我认为到目前为止答案中还没有提到以下几点,我认为它们值得一提。


地图可以更大

在Chrome中,我可以使用Map获得1670万个键/值对,而使用常规对象可以获得1110万个键/值对。带地图的情侣数量几乎增加了50%。它们在崩溃前都占用了大约2gb的内存,所以我认为可能与chrome的内存限制有关(是的,尝试填充2个地图,在崩溃前你只能得到830万对)。你可以用下面的代码自己测试它(显然,分开运行它们,而不是同时运行):

var m = new Map();
var i = 0;
while(1) {
    m.set(((10**30)*Math.random()).toString(36), ((10**30)*Math.random()).toString(36));
    i++;
    if(i%1000 === 0) { console.log(i/1000,"thousand") }
}
// versus:
var m = {};
var i = 0;
while(1) {
    m[((10**30)*Math.random()).toString(36)] = ((10**30)*Math.random()).toString(36);
    i++;
    if(i%1000 === 0) { console.log(i/1000,"thousand") }
}

对象已经有了一些属性/键

这个问题以前让我犯过错误。常规对象有toString, constructor, valueOf, hasOwnProperty, isPrototypeOf和其他一堆预先存在的属性。对于大多数用例来说,这可能不是一个大问题,但它曾经给我带来过问题。

地图会变慢:

由于.get函数调用开销和缺乏内部优化,Map在某些任务中可能比普通的旧JavaScript对象慢得多。