对比学习 Array.prototype.map 与 js的 map

一、简介

map 对象保存键值对。任何值(对象或原始值)都可以作为一个键或一个值。

二、语法

1
new Map([iterable])

2.1 参数

iterable可以是数组或者其他iterabled对象,其元素为键值对(两个元素的数组,例如[[1: ‘one’],[2: ‘two’]])。每个键值对都会添加到 新的Mapnull 会被当作 undefined

三、 描述

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

键的相等(Key equality)

键的比较是基于”Same ValueZero”算法: NaN 是与 NaN 相等的(虽然 NaN !== NaN),剩下所有其他的值是根据 === 运算符的结果判断是否相等。在目前的ECMAScript规范中,-0 和 +0 被认为是相等的,尽管在早期的草案中并不是这样。

Objects 和 Maps的比较

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

1、 一个Object的键只能是字符串或者Symbols,但一个Map的键可以是任意值,包括函数、对象、基本类型。
2、 Map中的键值是有序的,而添加到对象中的键则不是。因此,当对他进行遍历时,Map对象时按插入的顺序返回键值。
3、 你可以通过size属性直接获取一个Map的键值对个数,而Object的键值对个数只能手动计算。
4、 Map可直接迭代,而Object的迭代需要先获取他的键值数组,然后再进行迭代。
5、 Object都有自己的原型,原型链上的键名有可能和你自己对象上的设置的键名产生冲突。虽然ES5开始可以用 map = Object.create(null) 来创建一个没有原型的对象,但是这种用法不太常见。
6、 Map在涉及频繁增删键值对的场景下会有些性能优势。

四、属性

1
Map.length

属性length的值为0

1
get Map[@@species]

本构造函数用于创建派生对象

1
Map.prototype

表示Map构造器的原型。允许添加属性从而应用于所有的Map对象

五、Map 实例

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

属性

1
Map.prototype.constructor

返回一个函数,它创建了实例的原型。默认时Map函数

1
Map.prototype.size

返回Map对象的键/值对的数量

方法

1
Map.prototype.clear()

移除Map对象的所有键/值对。

1
Map.prototype.delete(key)

如果Map对象中存在该元素,则移除它并返回true;否则如果该元素不存在则返回false

1
Map.prototype.entries()

返回一个新的 Iterator 对象,它按插入顺序包含了Map对象中每个元素的[key,value]数组。

1
Map.prototype.forEach(callbackFn[, thisArg])

按插入顺序,为Map对象里的每一键值对调用一次callbackFn函数。如果forEach 提供了thisArg,它将在每次回调中作为this值。

1
Map.prototype.get(key)

返回键对应的值,如果不存在,则返回undefined

1
Map.prototype.has(key)

返回一个布尔值,表示Map实例是否包含键对应的值。

1
Map.prototype.keys()

返回一个新的 Iterator对象,他按插入顺序包含了Map对象中的每个元素的

1
Map.prototype.set(key,value)

设置Map对象中键的值。返回该Map对象。

1
Map.prototype.values()

返回一个新的Iterator对象,他按插入顺序包含了Map对象中每个元素的

1
Map.prototype[@@iterator]()

返回一个新的 Iterator对象,他按插入顺序包含了Map对象中的每个元素的[key,value]数组

六、示例

使用Map对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var myMap = new Map();
var keyObj = {},
keyFunc = function() {},
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' 关联的值
myMap.get({}); // undefined,因为 keyObj !== {}
myMap.get(function() {}); // undefined, 因为keyFunc !== funcetion() {}

将NaN作为Map的键

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

1
2
3
4
5
6
var myMap = new Map();
myMap.set(NaN, 'not a number');
MyMap.get(NaN); // 'not a number'

var otherNaN = Number('foo');
myMap.get(otherNaN); // 'not a number'

使用 for…of 方法迭代Map

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var myMap = new Map();
myMap.set(0, 'zero');
myMap.set(1, 'one');
for(var [key, value] of myMap) {
console.log(key + ' = ' + value);
}
// 将会展示两个log.一个是'0 = zero' 另一个是 '1 = one'

for (var key of myMap.keys()) {
console.log(key);
}
// 将会展示两个log,一个是'0',一个是'1'

for(var value of myMap.values()) {
console.log(value);
}
// 将会展示两个log,一个是'zero' 另一个是'one'

for(var [key, value] of myMap.extries()) {
console.log(key + ' = ' + value);
}
// 将会展示两个log,一个是 0 = zero 另一个是 1 = one

使用forEach 方法迭代Map

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

1
2
3
4
myMap.forEach(function(value, key) {
console.log(key + ' = ' + value);
}, myMap);
// 将会展示两个logs 一个是 0 = zero 另一个是 1 = one

Map与数组的关系

1
2
3
4
5
6
7
8
9
10
11
12
13
var kvArray = [["key1", "value1"], ["key2", "value2"]];

// 使用常规的Map构造函数可以将一个二维键值对数组转换成一个Map对象

var myMap = new Map(kvArray);

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

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

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

复制或合并Maps

Map能像数组一样被复制

1
2
3
4
5
var original = new Map([1, 'one']);
var clone = new Map(original);

console.log(clone.get(1)); // one
console.log(original === clone); // false. Useful for shallow comparison

请记住,数据本身未被克隆

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var first = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three']
]);
var second = new Map([
[1, 'uno'],
[2, 'doc']
]);

// 合并两个Map对象时,如果有重复的键值,则后面的会覆盖前面的

// 展开运算符本质上是将Map对象转换成数组

var merged = new Map([...first, ...second]);
console.log(merged.get(1)); // uno
console.log(merged.get(2)); // doc
console.log(merged.get(3)); // three

Map对象也能与数组合并

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var first = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three']
]);
var second = new Map([
[1, 'uno'],
[2, 'doc']
]);

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