Map

Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值) 都可以作为一个键或一个值。

描述

一个Map对象在迭代时会根据对象中元素的插入顺序来进行 — 一个  for...of 循环在每次迭代后会返回一个形式为[key,value]的数组。

键的相等(Key equality)

  • 键的比较是基于 sameValueZero 算法:
  • NaN 是与 NaN 相等的(虽然 NaN !== NaN),剩下所有其它的值是根据 === 运算符的结果判断是否相等。
  • 在目前的ECMAScript规范中,-0+0被认为是相等的,尽管这在早期的草案中并不是这样。有关详细信息,请参阅浏览器兼容性 表中的“Value equality for -0 and 0”。

Objects 和 maps 的比较

Objects 和 Maps 类似的是,它们都允许你按键存取一个值、删除键、检测一个键是否绑定了值。因此(并且也没有其他内建的替代方式了)过去我们一直都把对象当成 Maps 使用。不过 Maps 和 Objects 有一些重要的区别,在下列情况里使用 Map 会是更好的选择:

Map Object
意外的键 Map 默认情况不包含任何键。只包含显式插入的键。

一个 Object 有一个原型, 原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。

注意: 虽然 ES5 开始可以用 Object.create(null) 来创建一个没有原型的对象,但是这种用法不太常见。

键的类型 一个 Map的键可以是任意值,包括函数、对象或任意基本类型。 一个Object 的键必须是一个 String 或是Symbol
键的顺序

Map 中的 key 是有序的。因此,当迭代的时候,一个 Map 对象以插入的顺序返回键值。

一个 Object 的键是无序的

注意:自ECMAScript 2015规范以来,对象确实保留了字符串和Symbol键的创建顺序; 因此,在只有字符串键的对象上进行迭代将按插入顺序产生键。

Size  Map 的键值对个数可以轻易地通过size 属性获取 Object 的键值对个数只能手动计算
迭代 Mapiterable 的,所以可以直接被迭代。 迭代一个Object需要以某种方式获取它的键然后才能迭代。
性能

在频繁增删键值对的场景下表现更好。

在频繁添加和删除键值对的场景下未作出优化。

构造函数

Map()
创建 Map 对象

属性

Map.length
属性 length 的值为 0 。
想要计算一个Map 中的条目数量, 使用 Map.prototype.size.
get Map[@@species]
本构造函数用于创建派生对象。
Map.prototype
表示 Map 构造器的原型。 允许添加属性从而应用于所有的 Map 对象。

Map 实例

所有的 Map 对象实例都会继承 Map.prototype

属性

Map.prototype.constructor
返回一个函数,它创建了实例的原型。默认是Map函数。
Map.prototype.size
返回Map对象的键/值对的数量。

方法

Map.prototype.clear()
移除Map对象的所有键/值对 。
Map.prototype.delete(key)
如果 Map 对象中存在该元素,则移除它并返回 true;否则如果该元素不存在则返回 false。随后调用 Map.prototype.has(key) 将返回 false
Map.prototype.entries()
返回一个新的 Iterator 对象,它按插入顺序包含了Map对象中每个元素的 [key, value] 数组
Map.prototype.forEach(callbackFn[, thisArg])
按插入顺序,为 Map对象里的每一键值对调用一次callbackFn函数。如果为forEach提供了thisArg,它将在每次回调中作为this值。
Map.prototype.get(key)
返回键对应的值,如果不存在,则返回undefined。
Map.prototype.has(key)
返回一个布尔值,表示Map实例是否包含键对应的值。
Map.prototype.keys()
返回一个新的 Iterator对象, 它按插入顺序包含了Map对象中每个元素的
Map.prototype.set(key, value)
设置Map对象中键的值。返回该Map对象。
Map.prototype.values()
返回一个新的Iterator对象,它按插入顺序包含了Map对象中每个元素的
Map.prototype[@@iterator]()
返回一个新的Iterator对象,它按插入顺序包含了Map对象中每个元素的 [key, value] 数组

示例

使用 Map 对象

let myMap = new Map();
 
let keyObj = {};
let keyFunc = function() {};
let keyString = 'a string';
 
// 添加键
myMap.set(keyString, "和键'a string'关联的值");
myMap.set(keyObj, "和键keyObj关联的值");
myMap.set(keyFunc, "和键keyFunc关联的值");
 
myMap.size; // 3
 
// 读取值
myMap.get(keyString);    // "和键'a string'关联的值"
myMap.get(keyObj);       // "和键keyObj关联的值"
myMap.get(keyFunc);      // "和键keyFunc关联的值"
 
myMap.get('a string');   // "和键'a string'关联的值"
                         // 因为keyString === 'a string'
myMap.get({});           // undefined, 因为keyObj !== {}
myMap.get(function() {}); // undefined, 因为keyFunc !== function () {}

将 NaN 作为 Map 的键

NaN 也可以作为Map对象的键。虽然 NaN 和任何值甚至和自己都不相等(NaN !== NaN 返回true),但下面的例子表明,NaN作为Map的键来说是没有区别的:

let myMap = new Map();
myMap.set(NaN, "not a number");

myMap.get(NaN); // "not a number"

let otherNaN = Number("foo");
myMap.get(otherNaN); // "not a number"

使用 for..of 方法迭代 Map

Map可以使用for..of循环来实现迭代:

let myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one");
for (let [key, value] of myMap) {
  console.log(key + " = " + value);
}
// 将会显示两个log。一个是"0 = zero"另一个是"1 = one"

for (let key of myMap.keys()) {
  console.log(key);
}
// 将会显示两个log。 一个是 "0" 另一个是 "1"

for (let value of myMap.values()) {
  console.log(value);
}
// 将会显示两个log。 一个是 "zero" 另一个是 "one"

for (let [key, value] of myMap.entries()) {
  console.log(key + " = " + value);
}
// 将会显示两个log。 一个是 "0 = zero" 另一个是 "1 = one"

使用 forEach() 方法迭代 Map

Map也可以通过forEach()方法迭代:

myMap.forEach(function(value, key) {
  console.log(key + " = " + value);
})
// 将会显示两个logs。 一个是 "0 = zero" 另一个是 "1 = one"

Map 与数组的关系

let kvArray = [["key1", "value1"], ["key2", "value2"]];

// 使用常规的Map构造函数可以将一个二维键值对数组转换成一个Map对象
let myMap = new Map(kvArray);

myMap.get("key1"); // 返回值为 "value1"

// 使用Array.from函数可以将一个Map对象转换成一个二维键值对数组
console.log(Array.from(myMap)); // 输出和kvArray相同的数组

// 更简洁的方法来做如上同样的事情,使用展开运算符
console.log([...myMap]);

// 或者在键或者值的迭代器上使用Array.from,进而得到只含有键或者值的数组
console.log(Array.from(myMap.keys())); // 输出 ["key1", "key2"]

复制或合并 Maps

Map 能像数组一样被复制:

let original = new Map([
  [1, 'one']
]);

let clone = new Map(original);

console.log(clone.get(1)); // one
console.log(original === clone); // false. 浅比较 不为同一个对象的引用

重要:请记住,数据本身未被克隆。

Map对象间可以进行合并,但是会保持键的唯一性。

let first = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],
]);

let second = new Map([
  [1, 'uno'],
  [2, 'dos']
]);

// 合并两个Map对象时,如果有重复的键值,则后面的会覆盖前面的。
// 展开运算符本质上是将Map对象转换成数组。
let merged = new Map([...first, ...second]);

console.log(merged.get(1)); // uno
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three

Map对象也能与数组合并:

let first = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],
]);

let second = new Map([
  [1, 'uno'],
  [2, 'dos']
]);

// Map对象同数组进行合并时,如果有重复的键值,则后面的会覆盖前面的。
let merged = new Map([...first, ...second, [1, 'eins']]);

console.log(merged.get(1)); // eins
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three

使用说明

请注意!为Map设置对象属性也是可以的,但是可能引起大量的混乱。

所以,你还是可以这样做...

let wrongMap = new Map()
wrongMap['bla'] = 'blaa'
wrongMap['bla2'] = 'blaaa2'

console.log(wrongMap)  // Map { bla: 'blaa', bla2: 'blaaa2' }

...但是,这样做的话,它的行为会不符合预期:

wrongMap.has('bla')    // false
wrongMap.delete('bla') // false
console.log(wrongMap)  // Map { bla: 'blaa', bla2: 'blaaa2' }

无论如何,和正确用法比较起来,几乎没有什么不同: 

let myMap = new Map()
myMap.set('bla','blaa')
myMap.set('bla2','blaa2')
console.log(myMap)  // Map { 'bla' => 'blaa', 'bla2' => 'blaa2' }

myMap.has('bla')    // true
myMap.delete('bla') // true
console.log(myMap)  // Map { 'bla2' => 'blaa2' }

规范

Specification
ECMAScript (ECMA-262)
Map

浏览器兼容性

Update compatibility data on GitHub
DesktopMobileServer
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome for AndroidFirefox for AndroidOpera for AndroidSafari on iOSSamsung InternetNode.js
MapChrome Full support 38Edge Full support 12Firefox Full support 13IE Full support 11Opera Full support 25Safari Full support 8WebView Android Full support 38Chrome Android Full support 38Firefox Android Full support 14Opera Android Full support 25Safari iOS Full support 8Samsung Internet Android Full support 3.0nodejs Full support 0.12
Full support 0.12
Full support 0.10
Disabled
Disabled From version 0.10: this feature is behind the --harmony runtime flag.
Map() constructorChrome Full support 38Edge Full support 12Firefox Full support 13IE Full support 11Opera Full support 25Safari Full support 8WebView Android Full support 38Chrome Android Full support 38Firefox Android Full support 14Opera Android Full support 25Safari iOS Full support 8Samsung Internet Android Full support 3.0nodejs Full support 0.12
Full support 0.12
Full support 0.10
Disabled
Disabled From version 0.10: this feature is behind the --harmony runtime flag.
clearChrome Full support 38Edge Full support 12Firefox Full support 19IE Full support 11Opera Full support 25Safari Full support 8WebView Android Full support 38Chrome Android Full support 38Firefox Android Full support 19Opera Android Full support 25Safari iOS Full support 8Samsung Internet Android Full support 3.0nodejs Full support 0.12
deleteChrome Full support 38Edge Full support 12Firefox Full support 13IE Full support 11Opera Full support 25Safari Full support 8WebView Android Full support 38Chrome Android Full support 38Firefox Android Full support 14Opera Android Full support 25Safari iOS Full support 8Samsung Internet Android Full support 3.0nodejs Full support 0.12
Full support 0.12
Full support 0.10
Disabled
Disabled From version 0.10: this feature is behind the --harmony runtime flag.
entriesChrome Full support 38Edge Full support 12Firefox Full support 20IE No support NoOpera Full support 25Safari Full support 8WebView Android Full support 38Chrome Android Full support 38Firefox Android Full support 20Opera Android Full support 25Safari iOS Full support 8Samsung Internet Android Full support 3.0nodejs Full support 0.12
forEachChrome Full support 38Edge Full support 12Firefox Full support 25IE Full support 11Opera Full support 25Safari Full support 8WebView Android Full support 38Chrome Android Full support 38Firefox Android Full support 25Opera Android Full support 25Safari iOS Full support 8Samsung Internet Android Full support 3.0nodejs Full support 0.12
getChrome Full support 38Edge Full support 12Firefox Full support 13IE Full support 11Opera Full support 25Safari Full support 8WebView Android Full support 38Chrome Android Full support 38Firefox Android Full support 14Opera Android Full support 25Safari iOS Full support 8Samsung Internet Android Full support 3.0nodejs Full support 0.12
Full support 0.12
Full support 0.10
Disabled
Disabled From version 0.10: this feature is behind the --harmony runtime flag.
hasChrome Full support 38Edge Full support 12Firefox Full support 13IE Full support 11Opera Full support 25Safari Full support 8WebView Android Full support 38Chrome Android Full support 38Firefox Android Full support 14Opera Android Full support 25Safari iOS Full support 8Samsung Internet Android Full support 3.0nodejs Full support 0.12
Full support 0.12
Full support 0.10
Disabled
Disabled From version 0.10: this feature is behind the --harmony runtime flag.
Key equality for -0 and 0Chrome Full support 38Edge Full support 12Firefox Full support 29IE No support NoOpera Full support 25Safari Full support 9WebView Android Full support 38Chrome Android Full support 38Firefox Android Full support 29Opera Android Full support 25Safari iOS Full support 9Samsung Internet Android Full support 3.0nodejs Full support 4.0.0
keysChrome Full support 38Edge Full support 12Firefox Full support 20IE No support NoOpera Full support 25Safari Full support 8WebView Android Full support 38Chrome Android Full support 38Firefox Android Full support 20Opera Android Full support 25Safari iOS Full support 8Samsung Internet Android Full support 3.0nodejs Full support 0.12
setChrome Full support 38Edge Full support 12Firefox Full support 13IE Partial support 11
Notes
Partial support 11
Notes
Notes Returns 'undefined' instead of the 'Map' object.
Opera Full support 25Safari Full support 8WebView Android Full support 38Chrome Android Full support 38Firefox Android Full support 14Opera Android Full support 25Safari iOS Full support 8Samsung Internet Android Full support 3.0nodejs Full support 0.12
Full support 0.12
Full support 0.10
Disabled
Disabled From version 0.10: this feature is behind the --harmony runtime flag.
sizeChrome Full support 38Edge Full support 12Firefox Full support 19
Notes
Full support 19
Notes
Notes From Firefox 13 to Firefox 18, the size property was implemented as a Map.prototype.size() method, this has been changed to a property in later versions conform to the ECMAScript 2015 specification.
IE Full support 11Opera Full support 25Safari Full support 8WebView Android Full support 38Chrome Android Full support 38Firefox Android Full support 19
Notes
Full support 19
Notes
Notes From Firefox 13 to Firefox 18, the size property was implemented as a Map.prototype.size() method, this has been changed to a property in later versions conform to the ECMAScript 2015 specification.
Opera Android Full support 25Safari iOS Full support 8Samsung Internet Android Full support 3.0nodejs Full support 0.12
valuesChrome Full support 38Edge Full support 12Firefox Full support 20IE No support NoOpera Full support 25Safari Full support 8WebView Android Full support 38Chrome Android Full support 38Firefox Android Full support 20Opera Android Full support 25Safari iOS Full support 8Samsung Internet Android Full support 3.0nodejs Full support 0.12
@@iteratorChrome Full support 43Edge Full support 12Firefox Full support 36
Full support 36
No support 27 — 36
Notes Alternate Name
Notes A placeholder property named @@iterator is used.
Alternate Name Uses the non-standard name: @@iterator
No support 17 — 27
Notes Alternate Name
Notes A placeholder property named iterator is used.
Alternate Name Uses the non-standard name: iterator
IE No support NoOpera Full support 30Safari Full support 10WebView Android Full support 43Chrome Android Full support 43Firefox Android Full support 36
Full support 36
No support 27 — 36
Notes Alternate Name
Notes A placeholder property named @@iterator is used.
Alternate Name Uses the non-standard name: @@iterator
No support 17 — 27
Notes Alternate Name
Notes A placeholder property named iterator is used.
Alternate Name Uses the non-standard name: iterator
Opera Android Full support 30Safari iOS Full support 10Samsung Internet Android Full support 4.0nodejs Full support 0.12
@@speciesChrome Full support 51Edge Full support 13Firefox Full support 41IE No support NoOpera Full support 38Safari Full support 10WebView Android Full support 51Chrome Android Full support 51Firefox Android Full support 41Opera Android Full support 41Safari iOS Full support 10Samsung Internet Android Full support 5.0nodejs Full support 6.5.0
Full support 6.5.0
Full support 6.0.0
Disabled
Disabled From version 6.0.0: this feature is behind the --harmony runtime flag.
@@toStringTagChrome Full support 44Edge Full support 79Firefox Full support 51IE No support NoOpera No support NoSafari No support NoWebView Android Full support 44Chrome Android Full support 44Firefox Android Full support 51Opera Android No support NoSafari iOS No support NoSamsung Internet Android Full support 4.0nodejs Full support 6.0.0

Legend

Full support  
Full support
Partial support  
Partial support
No support  
No support
See implementation notes.
See implementation notes.
User must explicitly enable this feature.
User must explicitly enable this feature.
Uses a non-standard name.
Uses a non-standard name.

相关链接