ES6
给我们提供了 Map
数据结构,它类似于对象,用于保存键值对。不同的是,Map
中键的范围不限于字符串类型,各种类型的值(包括对象)都可以当作一个键或一个值。也就是说,Object
结构提供了“字符串—值”的对应,Map
结构提供了“值—值”的对应,是一种更完善的 Hash
结构实现。如果需要用到“键值对”的数据结构,Map
比 Object
更合适。
Map和Object的区别
Object
中的键只能是字符串或者Symbols
类型,但Map
中的键可以是任意值。Map
中的键值是有序的(FIFO 原则),而添加到对象中的键则不是。Map
的键值对个数可以从size
属性获取,而Object
的键值对个数只能手动计算。Object
都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。
创建Map
Map
是 ES6
提供给我们的构造函数,本质上是键值对的集合。任何值(对象或者原始值) 都可以作为一个键或一个值。
示例:
我们可以通过 Map()
方法来创建一个空 Map
:
let map = new Map();
console.log(map); // 输出:Map {}
上述代码中,我们可以在创建时初始化 Map
时,Map
可以接收数组作为参数,并且数组成员也是一个个数组,其中包含两个元素,一个表示键,一个表示值。
示例:
例如下面这个 Map
中有两个键值对:
let map = new Map([["name","xkd"],['age','18']]);
console.log(map);
// 输出:Map { 'name' => 'xkd', 'age' => '18' }
第一个键值对中键为 name
,值为 xkd
,第二个键值对中键为 age
,值为 18
。
Map的方法和属性
set()方法
set()
方法用于设置所对应的键值对,然后返回整个 Map
结构,如果已经有值,则键值会被更新,否则就会生成新的键。
示例:
let map = new Map();
map.set('name', 'xkd');
console.log(map); // 输出:Map { 'name' => 'xkd' }
如果要设置多个键值对,则可以执行多次 set
方法。
示例:
例如向 map
中添加三个键值对:
let map = new Map();
map.set('a', 1);
map.set('b', 2);
map.set('c', 3);
console.log(map);
// 输出:Map { 'a' => 1, 'b' => 2, 'c' => 3 }
get()方法
get()
方法用于读取 key
对应的键值,如果找不到 key
,则返回 undefined
。
示例:
下面代码中我们通过 set()
方法为 Map
设置键值对,然后通过 get()
方法获取指定键对应的值:
let map = new Map();
map.set('a', 1);
map.set('b', 2);
map.set('c', 3);
console.log(map.get('a')); // 输出:1
console.log(map.get('b')); // 输出:2
console.log(map.get('c')); // 输出:3
console.log(map.get('d')); // 输出:undefined
可以看到,如果我们想要取读取的键值在 map
中不存在,例如 d
,那么最终会输出 undefined
。
has()方法
has()
方法返回一个布尔值,表示某个键是否在 Map
数据结构中。在则返回 true
,不在则返回 false
。
示例:
let map = new Map();
map.set('a', 1);
map.set('b', 2);
map.set('c', 3);
console.log(map.has('a')); // 输出:true
console.log(map.has('b')); // 输出:true
console.log(map.has('name')); // 输出:false
上述代码中,我们先通过 set()
方法 向 map
中添加了三个键值对,但是很明显 a
、b
存在 map
数据结构中,而 name
不存在。
delete()方法
delete()
方法用于删除某个键,删除成功返回 true
,如果删除失败则返回 false
。
示例:
let map = new Map();
map.set('a', 1);
map.set('b', 2);
map.set('c', 3);
console.log(map.delete("a")); // 输出:true
console.log(map.delete("age")); // 输出:false
可以看到,因为 a
是 map
中的键,所以删除 a
时返回 true
值。而 age
不是 map
中的键,所以返回 false
。
clear()方法
clear()
方法用于清除 Map
中所有成员,没有返回值。
示例:
当 Map
中的成员很多是,如果使用 delete()
方法一个键一个键删除会很麻烦,所以如果要清除 Map
中所有的成员,可以直接使用 clear()
方法:
let map = new Map();
map.set('a', 1);
map.set('b', 2);
map.set('c', 3);
map.clear();
console.log(map); // 输出:Map {}
size属性
size
属性可以用于返回 Map
中成员总数。
示例:
let map = new Map();
map.set('a', 1);
map.set('b', 2);
map.set('c', 3);
console.log(map.size); // 输出:3
Map 遍历方法
Map
原生提供三个遍历器生成函数和一个遍历方法。
示例:
我们先创建并初始化一个 Map
:
let map1 = new Map([
["x", 1],
["k", 2],
["d", 3]
]);
keys()方法
keys()
方法是一个用于返回键名的遍历器。
示例:
例如通过 keys()
方法遍历 map1
:
for(let key of map1.keys()){
console.log(key);
}
输出:
x
k
d
从上述输出中可以看出,成功遍历了 map1
中的所有键值。
values()方法
values()
方法与 keys()
对应,是一个用于返回键值的遍历器。
示例:
例如通过 values()
方法遍历 map1
:
for(let v of map1.values()){
console.log(v);
}
输出:
1
2
3
entries()方法
如果我们又想要遍历 Map
中的键又想要遍历 Map
中的值,可以使用 entries()
方法,entries()
方法用于返回所有成员的遍历器。
示例:
let map1 = new Map([
["x", 1],
["k", 2],
["d", 3]
]);
for(let [key,value] of map1.entries()){
console.log(key+':'+value);
}
输出:
x:1
k:2
d:3
forEach()方法
forEach()
方法用于遍历 Map
的所有成员,第二个参数绑定 this
。
示例:
map1.forEach(function(value, key){
console.log(key+':'+value);
});
输出:
x:1
k:2
d:3
Map的类型转换
Map
类型可以与其他的数据类型进行转换,我们一起来看一下。
示例:
Map
转为数组类型:可以使用扩展运算符...
来实现。
let map1 = new Map();
map1.set("name", "xkd");
console.log([...map1]);
// 输出:[ [ 'name', 'xkd' ] ]
- 数组类型转为
Map
:将数组传入Map
构造函数即可。
let map1 = new Map([
["x", 1],
["k", 2],
["d", 3]
]);
Map
转为对象:如果所有Map
的键都是字符串,它可以无损地转为对象。如果有非字符串的键名,那么这个键名会被转成字符串,再作为对象的键名。
function strMapToObj(strMap) {
let obj = Object.create(null);
for (let [k,v] of strMap) {
obj[k] = v;
}
return obj;
}
let map1 = new Map();
map1.set("name", "xkd");
console.log(strMapToObj(map1));
// 输出:[Object: null prototype] { name: 'xkd' }
- 对象转为
Map
:可以使用Object.entries()
方法来实现。
let obj = {"a":1, "b":2};
let map1 = new Map(Object.entries(obj));
console.log(map1);
// 输出:Map { 'a' => 1, 'b' => 2 }
Map
转为JSON
:分为两种情况,一种是Map
键名为字符串,这可以转为对象JSON
。
function strMapToObj(strMap) {
let obj = Object.create(null);
for (let [k,v] of strMap) {
obj[k] = v;
}
return obj;
}
function strMapToJson(strMap) {
return JSON.stringify(strMapToObj(strMap));
}
let map1 = new Map().set('a', 1).set('b', 2);
console.log(strMapToJson(map1)); // 输出:{"a":1,"b":2}
还有一种情况是,Map
中的键名有非字符串,这时可以选择转为数组 JSON
。
function mapToArrayJson(map) {
return JSON.stringify([...map]);
}
let map1 = new Map().set(true, 1).set({b: 2}, 2);
console.log(mapToArrayJson(map1)); // 输出:[[true,1],[{"b":2},2]]
JSON
转为Map
:如果所有键名都是字符串则可以像下面这个。
function objToStrMap(obj) {
let strMap = new Map();
for (let k of Object.keys(obj)) {
strMap.set(k, obj[k]);
}
return strMap;
}
function jsonToStrMap(jsonStr) {
return objToStrMap(JSON.parse(jsonStr));
}
console.log(jsonToStrMap('{"a": 1, "b": 2}'));
// 输出:Map { 'a' => 1, 'b' => 2 }
如果整个 JSON
就是一个数组,且每个数组成员本身,又是一个有两个成员的数组。它可以一一对应地转为 Map
。这往往是 Map
转为数组 JSON
的逆操作。
function jsonToMap(jsonStr) {
return new Map(JSON.parse(jsonStr));
}
console.log(jsonToMap('[[1, 100],[{"age":18},[20]]]'));
// 输出:Map { 1 => 100, { age: 18 } => [ 20 ] }