--- title: Map slug: Web/JavaScript/Reference/Global_Objects/Map translation_of: Web/JavaScript/Reference/Global_Objects/Map tags: - Class - ECMAScript 2015 - JavaScript - Map - Reference - Polyfill browser-compat: javascript.builtins.Map ---
Map
对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者{{Glossary("Primitive", "原始值")}}) 都可以作为一个键或一个值。
一个Map对象在迭代时会根据对象中元素的插入顺序来进行 — 一个 {{jsxref("Statements/for...of", "for...of")}} 循环在每次迭代后会返回一个形式为[key,value]的数组。
sameValueZero
算法:NaN
相等的(虽然 NaN !== NaN
),剩下所有其它的值是根据 ===
运算符的结果判断是否相等。-0
和+0
被认为是相等的,尽管这在早期的草案中并不是这样。有关详细信息,请参阅浏览器兼容性 表中的“Value equality for -0 and 0”。{{jsxref("Object", "Objects")}} 和 Maps
类似的是,它们都允许你按键存取一个值、删除键、检测一个键是否绑定了值。因此(并且也没有其他内建的替代方式了)过去我们一直都把对象当成 Maps
使用。不过 Maps
和 Objects
有一些重要的区别,在下列情况里使用 Map
会是更好的选择:
Map | Object | |
---|---|---|
意外的键 | Map 默认情况不包含任何键。只包含显式插入的键。 |
一个 注意: 虽然 ES5 开始可以用 |
键的类型 | 一个 Map 的键可以是任意值,包括函数、对象或任意基本类型。 |
一个Object 的键必须是一个 {{jsxref("String")}} 或是{{jsxref("Symbol")}}。 |
键的顺序 |
|
一个 注意:自ECMAScript 2015规范以来,对象确实保留了字符串和Symbol键的创建顺序; 因此,在只有字符串键的对象上进行迭代将按插入顺序产生键。 |
Size | Map 的键值对个数可以轻易地通过{{jsxref("Map.prototype.size", "size")}} 属性获取 |
Object 的键值对个数只能手动计算 |
迭代 | Map 是 iterable 的,所以可以直接被迭代。 |
迭代一个Object 需要以某种方式获取它的键然后才能迭代。 |
性能 |
在频繁增删键值对的场景下表现更好。 |
在频繁添加和删除键值对的场景下未作出优化。 |
Map
对象Map.length
Map
中的条目数量, 使用 {{jsxref("Map.prototype.size")}}.Map
构造器的原型。 允许添加属性从而应用于所有的 Map
对象。Map
实例所有的 Map
对象实例都会继承 {{jsxref("Map.prototype")}}。
{{page('zh-CN/Web/JavaScript/Reference/Global_Objects/Map/prototype','属性')}}
{{page('zh-CN/Web/JavaScript/Reference/Global_Objects/Map/prototype','方法')}}
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 |
---|
{{SpecName('ESDraft', '#sec-map-objects', 'Map')}} |
{{Compat("javascript.builtins.Map")}}