序言
首先这个 Map 不是数组操作方法的 map ,Set 也不是设置值的 set 。
Map 是一个带键的数据项的集合,就像一个 Object 一样。 但是它们最大的差别是 Map 允许任何类型的键(key)。
Set 是一个特殊的类型集合 —— “值的集合”(没有键),它的每一个值只能出现一次。
Map
Map基本属性
new Map()—— 创建 map。map.set(key, value)—— 根据键存储值。map.get(key)—— 根据键来返回值,如果map中不存在对应的key,则返回undefined。map.has(key)—— 如果key存在则返回true,否则返回false。map.delete(key)—— 删除指定键的值。map.clear()—— 清空 map。map.size—— 返回当前元素个数。
let map = new Map();
map.set('1', 'str1'); // 字符串键
map.set(1, 'num1'); // 数字键
map.set(true, 'bool1'); // 布尔值键
// 还记得普通的 Object 吗? 它会将键转化为字符串
// Map 则会保留键的类型,所以下面这两个结果不同:
alert( map.get(1) ); // 'num1'
alert( map.get('1') ); // 'str1'
alert( map.has(1) ); // 'true'
alert( map.size ); // 3
小结:Map 中**,**键不会被转换成字符串,键可以是任何类型,包括对象。map[key] 不是 Map 的正确方式,这样会将 map 视为 JavaScript 的 plain object,那么他就没有对象键了。
我们看一个对象键的案例
let coolFish = { name: "coolFish" };
// 存储每个用户的来访次数
let visitsCountMap = new Map();
// john 是 Map 中的键
visitsCountMap.set(coolFish, 123);
alert( visitsCountMap.get(coolFish) ); // 123
Map 是怎么比较键的?
Map 使用 SameValueZero 算法来比较键是否相等。它和严格等于 === 差不多,但区别是 NaN 被看成是等于 NaN。所以 NaN 也可以被用作键。
这个算法不能被改变或者自定义。
链式调用
每一次 map.set 调用都会返回 map 本身,所以我们可以进行链式调用。
map.set('1', 'str1')
.set(1, 'num1')
.set(true, 'bool1');
Map 迭代
如果要在 map 中使用循环,可以使用以下三个方法
- map.keys()遍历并返回所有的键。
- map.value()遍历并返回所有的值。
- map.entries()遍历并返回所有实体。
let coolFish = new Map([
['age', 24],
['sex', '男'],
['height',178]
]);
// 遍历所有的键(vegetables)
for (let vegetable of coolFish.keys()) {
alert(vegetable); // age, sex, height
}
// 遍历所有的值(amounts)
for (let amount of coolFish.values()) {
alert(amount); // 24, '男', 178
}
// 遍历所有的实体 [key, value]
for (let entry of coolFish) { // 与 recipeMap.entries() 相同
alert(entry); // ['age', 24]
}
迭代的顺序与插入值的顺序相同。与普通的 Object 不同,Map 保留了此顺序。
Object.entries: 从对象创建Map
当创建一个 Map 后,我们可以传入一个带有键值对的数组或者其他可迭代对象来进行初始化,如下所示
// 键值对 [key, value] 数组
let map = new Map([
['1', 'str1'],
[1, 'num1'],
[true, 'bool1']
]);
alert( map.get('1') ); // str1
如果我们想要从对象创建一个 Map ,我们就要用到 Object.entries(obj),该返回对象的键/值对数组,该数组格式完全按照 Map 所需的格式。
let obj = {
name: "coolFish",
age: 24
};
let map = new Map(Object.entries(obj));
alert( map.get('name') ); // coolFish
那么同样,我们也有一种方法 Object.fromEntries 可以让我们把一个具有 [key, value] 的数组创建一个对象。
let map = new Map();
map.set('banana', 1);
map.set('orange', 2);
map.set('meat', 4);
//let obj = Object.fromEntries(map.entries()); // 创建一个普通对象(plain object)(*)
let obj = Object.fromEntries(map); // 省掉 .entries()
// 完成了!
// obj = { banana: 1, orange: 2, meat: 4 }
alert(obj.orange); // 2
调用 map.entries() 将返回一个可迭代的键/值对,这刚好是 Object.fromEntries 所需要的格式。
Set
Set基本属性
它的主要方法如下:
new Set(iterable)—— 创建一个set,如果提供了一个iterable对象(通常是数组),将会从数组里面复制值到set中。set.add(value)—— 添加一个值,返回 set 本身set.delete(value)—— 删除值,如果value在这个方法调用的时候存在则返回true,否则返回false。set.has(value)—— 如果value在 set 中,返回true,否则返回false。set.clear()—— 清空 set。set.size—— 返回元素个数。
它的主要特点是,重复使用同一个值调用 set.add(value) 并不会发生什么改变。这就是 Set 里面的每一个值只出现一次的原因。
let set = new Set();
let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };
// visits,一些访客来访好几次
set.add(john);
set.add(pete);
set.add(mary);
set.add(john);
set.add(mary);
// set 只保留不重复的值
alert( set.size ); // 3
for (let user of set) {
alert(user.name); // John(然后 Pete 和 Mary)
alert(user); //{name: "John"}
}
Set 迭代(iteration)
我们可以使用 for..of 或者 forEach 来遍历 Set 。
let set = new Set(["oranges", "apples", "bananas"]);
for (let value of set) alert(value);
// 与 forEach 相同:
set.forEach((value, valueAgain, set) => {
alert(value);
});
我们可以发现,forEach 回调函数中,当前值和索引是同一个值,他们都是当前项,这是为了在特定情况下轻松的用 Set 替代 Map,反之亦然。
set.keys()—— 遍历并返回所有的值(returns an iterable object for values),set.values()—— 与set.keys()作用相同,这是为了兼容Map,set.entries()—— 遍历并返回所有的实体(returns an iterable object for entries)[value, value],它的存在也是为了兼容Map。
总结
Map —— 是一个带键的数据项的集合。
let coolFish = new Map([
['age', 24],
['sex', '男'],
['height',178]
]);
Set —— 是一组唯一值的集合。
function unique(arr) {
return Array.from(new Set(arr));
}
let values = ["Hare", "Krishna", "Hare", "Krishna",
"Krishna", "Krishna", "Hare", "Hare", ":-O"
];
alert( unique(values) ); // Hare, Krishna, :-O
以上就是 Map 和 Set 的基本特性和使用啦。