JavaScript 原有的表示“集合”的数据结构,主要是数组(Array
)和对象(Object
)。
在ES6,引入了Map
和 Set
。
Map
Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值)都可以作为键或值。(请查看mdn)
从定义上来看,Map
是一个带键的数据项的集合,就像一个 Object
一样。
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("string", "str1"); // 字符串键
map.set(1, "Number"); // 数字键
map.set(true, "Boolean"); // 布尔值键
map.set({"键名": "键值"}, "Object"); // 使用对象作为键
map.set(function() {}, "Function"); // 使用方法作为键
// 还记得普通的 Object 吗? 它会将键转化为字符串
// Map 则会保留键的类型,所以下面这两个结果不同:
console.log( map.get(1)); // 'num1'
console.log( map.get('1') ); // 'str1'
console.log( map.size ); // 5
Map
和 Object
都允许按键存取一个值、删除键、检测一个键是否绑定了值。
Map和Object的区别
Object
只能接收两种类型的键名:String
和Symbol
。
Map
能够接受任意类型的键名(包括函数、对象或任何原始值)。
let map =new Map();
// 链式调用
map.set(1, "Number").set(true, "Boolean").set({"键名": "键值"}, "Object")
map.set(function() {}, "Function")
console.log(map);
let obj = {
1: "Number",
true: "Boolean",
[{"键名": "键值"}]: "Object",
[function() {}]: "Function"
}
console.log(obj)
虽然说Object
可以接受其他类型的键名,当时js都会隐式地将其转换为字符串
Map
是可迭代对象,具备iterator接口,可以直接迭代。
Object
没有实现可迭代协议,是不能直接进行迭代的。
let map =new Map();
// 链式调用
map.set(1, "Number").set(true, "Boolean").set({"键名": "键值"}, "Object")
map.set(function() {}, "Function")
console.log(map);
for(let it of map) {
console.log(it)
}
Map
可以通过map.size()
直接获取当前元素个数。
Object
不能直接获取。一般是通过Object.keys()
返回对象中每一项key的数组,来获取当前对象的元素个数。
Map 迭代
如果要在 map 里使用循环,可以使用以下三个方法:
map.keys()
—— 遍历并返回一个包含所有键的可迭代对象。map.values()
—— 遍历并返回一个包含所有值的可迭代对象。map.entries()
—— 遍历并返回一个包含所有实体[key, value]
的可迭代对象。
// Map初始化要用二维数组
let map = new Map([
["string", "str1"],
["1", "Number"],
['tomatoes', 350],
['onion', 50]
]);
// 遍历所有的键
for (let vegetable of map.keys()) {
console.log(vegetable); // string, 1, tomatoes, onion
}
// 遍历所有的值
for (let amount of map.values()) {
console.log(amount); // str1, Number, 500, 350, 50
}
// 遍历所有的实体 [key, value]
for (let entry of map) { // 与 recipeMap.entries() 相同
console.log(entry);
}
// 此外,Map还有内建的forEach方法
// 对每个键值对 (key, value) 运行 forEach 函数
map.forEach( (value, key, map) => {
console.log(`${key}: ${value}`);
});
注意:Map
初始化要用二维数组!!
Object.entries:从对象Object创建 Map
Object.entries(obj)
方法返回对象的键/值对数组,该数组格式就是 Map
所需的格式。
let obj = {
name: "张三",
age: 18
}
// Object.entries返回键/值对数组
let objEntries = Object.entries(obj); // [ ["name","张三"], ["age", 18] ]
console.log(objEntries)
let map = new Map(objEntries)
console.log(map)
Object.fromEntries:从 Map 创建 对象Object
Object.fromEntries
方法:给定一个具有 [key, value] 键值对的数组,它会根据给定数组创建一个对象。
简而言之,使用 Object.fromEntries
把键/值对数组转成普通对象(plain object)。
let map = new Map([
["name", "张三"],
["age", 18],
])
let obj = Object.fromEntries(map)
console.log(obj) // {name: '张三', age: 18}
let obj1 = Object.fromEntries(map.entries());
console.log(obj1) // {name: '张三', age: 18}
上面两句代码的作用是一样的,因为 Object.fromEntries
期望得到一个可迭代对象作为参数,而不一定是数组。
Map
的标准迭代会返回跟 map.entries()
一样的键/值对。
因此,我们可以获得一个普通对象(plain object),其键/值对与 map 相同。
Set
Set 对象允许你存储任何类型(无论是原始值还是对象引用)的唯一值。(请查看mdn)
Set
对象是值的合集(collection)。集合(set)中的元素只会出现一次,即集合中的元素是唯一的。
Set
没有键!!
Set
是一种特殊的对象,长得像数组,但是用的是花括号!!
let set = new Set(["a", "b", "c"]);
console.log(set); // Set(3) {'a', 'b', 'c'}
console.log(typeof set); // object
console.log(set instanceof Set); // true
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
里面的每一个值只出现一次,所以 重复使用同一个值调用 set.add(value)
并不会发生什么改变。
Set
内部对唯一性检查进行了更好的优化。
let set = new Set();
let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };
// 重复使用一个值调用set.add
set.add(john);
set.add(pete);
set.add(mary);
set.add(john);
set.add(mary);
// set 只保留不重复的值
console.log(set)
console.log( set.size ); // 3
for (let it of set) {
console.lot(it.name); // John(然后 Pete 和 Mary)
}
Set 迭代(iteration)
let set = new Set(["oranges", "apples", "bananas"]);
for (let value of set) console.log(value);
// 与 forEach 相同:
set.forEach((value, valueAgain, set) => {
console.log(value);
});
注意:使用forEach
,同一个值在参数里出现了2次。
forEach
的回调函数有三个参数:一个 value,然后是 同一个值 valueAgain,最后是目标对象。
forEach
的回调函数有三个参数,是为了与 Map
兼容。
Map
中用于迭代的方法在 Set
中也同样支持:
set.keys()
—— 遍历并返回一个包含所有键的可迭代对象。set.values()
—— 遍历并返回一个包含所有值的可迭代对象。
因为Set
没有键(key),所以keys()
与values()
返回结果相同。这是为了兼容Map
。
let set = new Set(["oranges", "apples", "bananas"]);
console.log(set.keys()); // SetIterator {'oranges', 'apples', 'bananas'}
console.log(set.values()); // SetIterator {'oranges', 'apples', 'bananas'}
set.entries()
—— 遍历并返回一个包含所有的实体[value, value]
的可迭代对象。
Set
没有键(key),entries()
方法返回的是[value, value]
值值对,而不是[key, value]
键值对,这是为了兼容Map
。
let set = new Set(["oranges", "apples", "bananas"]);
console.log(set.entries()); // SetIterator {'oranges' => 'oranges', 'apples' => 'apples', 'bananas' => 'bananas'}
for(let it of set.entries()) {
console.log(it) // ['oranges', 'oranges'] and so on
}
将Set转为数组
使用Array.from
或者扩展运算符(...
)可以将Set
转为数组。
let set = new Set(["a", "b", "c"]);
console.log(set); // Set(3) {'a', 'b', 'c'}
let arr = Array.from(set); // ["a", "b", "c"]
let arr1 = [...set]; // ["a", "b", "c"]
将数组转为Set
// 方法1
let arr = ["a", "b", "c"]
let set = new Set(["a", "b", "c"]);
console.log(set); // Set(3) {'a', 'b', 'c'}
// 方法2:使用map()
let arr1 = [5, 5, 4, 6];
let set1 = new Set();
arr1.map(arrElement => set1.add(arrElement));
console.log(set1); // Set(3) {'a', 'b', 'c'}
合并Set
可以使用Set
的构造函数和...
运算符来合并两个Set。
let set1 = new Set(["a", "b", "c"]);
let set2 = new Set([1, 2, 3]);
// 合并set1、set2
let bigSet = new Set([...set1, ...set2]); // Set(6) {'a', 'b', 'c', 1, 2, 3}