Set 是叫集合的数据结构,Map 是叫字典的数据结构。
-
相同点:
Set和Map存储不重复的值。 -
不同点:
Set用[value, value]存储数据,Map用[key, value]存储数据。
1、总结
Set:
- 成员唯一、无序且不重复。
[value, value],键值与键名是一致的(或者说只有键值,没有键名)。- 遍历顺序按插入顺序。
- 可以遍历,方法有:
add、delete、has、size、clear。
Map:
- 本质上是键值对的集合,其中的键和值可以是任意类型的,类似集合。不同之处在于,
Map可以使用任意类型作为键,而对象只能使用字符串或Symbol类型作为键。 - 遍历顺序按插入顺序。
- 可以遍历,方法很多可以跟各种数据格式转换,方法有:
get、set、has、delete、size、clear。
WeakSet:
- 成员都是对象,其中每个值都是唯一的,并且没有特定的顺序。
- 成员都是弱引用,可以被垃圾回收机制回收,可以用来保存
DOM节点,不容易造成内存泄漏。 - 不能遍历,方法有
add、delete、has。 - 没有
size属性,因为其内容可能会随时被垃圾回收。
WeakMap:
- 只接受对象作为键名(
null除外),不接受其他类型的值作为键名,并且没有特定的顺序。它们的键必须是对象,而值可以是任何类型。 - 键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的。
- 不能遍历,方法有
get、set、has、delete。 - 没有
size属性,因为其内容可能会随时被垃圾回收。
2、Set
Set 本身是一个构造函数,用来生成 Set 数据结构。Set 函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。Set 对象允许你存储任何类型的值,无论是原始值或者是对象引用。它类似于数组,但是成员的值都是唯一的,没有重复的值。
2.1、介绍
Set类似数组,成员唯一。Set本身是构造函数,生成Set数据结构。Set可以存储任意类型的唯一值。Setkey的值和value的值一样
let s = new Set();
[1, 2, 3, 4, 5, 6, 1, 2].map(item=>{
s.add(item);
}); // [1, 2, 3, 4, 5, 6]
// 数组去重
let arr = new Set([1, 2, 3, 4, 3, 2, 4, 5]);
console.log([...new Set(arr)]);
// or
let arr = new Set([1, 2, 3, 4, 3, 2, 4, 5]);
console.log(Array.from(new Set(arr)));
注:Set 里面加入值时不会发送类型转换,及 5 和 '5' 是两个不同的值,Set 判断两个值是否相等类似于 ‘===’。
但判断 NaN 时,'===' 的判断 NaN 不等于 NaN,Set 的判断 NaN 等于 NaN。
2.2、操作
add(value):新增,相当于Array的push。
let arr = new Set([1, 2, 3, 4, 3, 2, 4, 5]);
arr.add(9); // 1,2,3,4,9
delete(value):存在就删除里面的value。
let arr = new Set([1, 2, 3, 4, 3, 2, 4, 5]);
arr.delete(1); // true, 删除成功
has(value):判断Set里面有没有这个值。
let arr = new Set([1, 2, 3, 4, 3, 2, 4, 5]);
arr.has(1); // true
clear():清空集合。
let arr = new Set([1, 2, 3, 4, 3, 2, 4, 5]);
arr.clear();
size:返回实例成员总数。
let arr = new Set([1, 2, 3, 4, 3, 2, 4, 5]);
arr.size; // 5
2.3、遍历
keys():返回一个包含集合中所有键的迭代器。
let arr = new Set([1, 2, 3, 4, (a = { a: 1, b: 2 })]);
arr.keys(); // SetIterator {1, 2, 3, 4, {…}}
values():返回一个包含集合中所有值得迭代器。
let arr = new Set([1, 2, 3, 4, (a = { a: 1, b: 2 })]);
arr.keys(); // SetIterator {1, 2, 3, 4, {…}}
entries():返回一个包含Set对象中所有元素得键值对迭代器。
let arr = new Set([1, 2, 3, 4, (a = { a: 1, b: 2 })]);
arr.entries(); // SetIterator {1 => 1, 2 => 2, 3 => 3, 4 => 4, {…} => {…}}
forEach():使用回调函数遍历每个成员。
let arr = new Set([1, 2, 3, 4, (a = { a: 1, b: 2 })]);
arr.forEach((item) => console.log(item)); // 1 2 3 4 { a: 1, b: 2 }
2.4、Array 和 Set 对比
Array的indexOf方法比Set的has方法效率低下。Set不含有重复值(可以利用这个特性实现对一个数组的去重)。Set通过delete方法删除某个值,而Array通过splice。两者的使用方便程度前者更优。Array的很多新方法map、filter、some、every等是Set没有的(但是通过两者可以互相转换来使用)。
2.5、Set 的应用
Array.from方法可以将Set结构转为数组。
const items = new Set([1, 2, 3, 4, 5]);
const array = Array.from(items);
- 数组去重
// 去除数组的重复成员
[...new Set(array)];
Array.from(new Set(array));
- 数组的
map和filter方法也可以间接用于Set。
let set = new Set([1, 2, 3])
set = new Set([...set].map((x) => x * 2))
// 返回Set结构:{2, 4, 6}
let set = new Set([1, 2, 3, 4, 5])
set = new Set([...set].filter((x) => x % 2 == 0))
// 返回Set结构:{2, 4}
- 实现并集
(Union)、交集(Intersect)和差集
let a = new Set([1, 2, 3])
let b = new Set([4, 3, 2])
// 并集
let union = new Set([...a, ...b])
// Set {1, 2, 3, 4}
// 交集
let intersect = new Set([...a].filter((x) => b.has(x)))
// set {2, 3}
// 差集
let difference = new Set([...a].filter((x) => !b.has(x)))
// Set {1}
3、Map
Map 中存储的是 key-value 形式的键值对, 其中的 key 和 value 可以是任何类型的, 即对象也可以作为 key。 Map 的出现,就是让各种类型的值都可以当作键。Map 提供的是 “值-值” 的对应,是一种更完善的 Hash 结构实现。
3.1、介绍
- Map是键值对的结构,查找速度极快。
- 类似于对象的数据结构,成员键是任何类型的值。
let a = new Map();
let obj = { a: 1 };
a.set('a', obj); // Map(1) {'a' => {…}}
a.get('a'); // {a: 1}
a.has('a'); // true
a.delete('a'); // true (删除成功)
a.has('a'); // false
3.2、实例属性和方法
size属性:返回Map结构的成员总数。set(key, value):set方法设置key所对应的键值,然后返回整个Map结构。如果key已经有值,则键值会被更新,否则就新生成该键,set方法返回的是Map本身,因此可以采用链式写法。get(key):get方法读取key对应的键值,如果找不到key,返回undefined。has(key):has方法返回一个布尔值,表示某个键是否在Map数据结构中。delete(key):delete方法删除某个键,返回true。如果删除失败,返回false。clear():clear方法清除所有成员,没有返回值。keys():返回以键为遍历器的对象。values():返回以值为遍历器的对象。entries():返回以键和值为遍历器的对象。forEach():使用回调函数遍历每个成员。
let map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
map.keys(); // MapIterator {1, 2, 3}
[...map.keys()]; // [1, 2, 3]
map.values(); // MapIterator {'one', 'two', 'three'}
[...map.values()]; // ['one', 'two', 'three']
map.entries(); // MapIterator {1 => 'one', 2 => 'two', 3 => 'three'}
[...map.entries()]; // [[1, 'one'], [2, 'two'], [3, 'three']]
map; // Map(3) {1 => 'one', 2 => 'two', 3 => 'three'}
[...map]; // [[1, 'one'], [2, 'two'], [3, 'three']]
Object.fromEntries()
Object.fromEntries() 方法是 Object.entries() 的逆操作,用于将一个键值对数组转为对象。
let map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
map; // Map(3) {1 => 'one', 2 => 'two', 3 => 'three'}
Object.fromEntries(map); // {1: 'one', 2: 'two', 3: 'three'}
应用场景:
- 将键值对的数据结构还原为对象,因此特别适合将
Map结构转为对象。 - 配合
URLSearchParams对象,将查询字符串转为对象。
Object.fromEntries(new URLSearchParams('name=jack&age=24')); // {name: 'jack', age: '24'}
3.3、Map 和 Object 的区别
Object对象有原型,也就是说他有默认的key值在对象上面,除非我们使用Object.create(null)创建一个没有原型的对象。- 在
Object对象中,只能把String和Symbol作为key值,但是在Map中,key值可以是任何基本类型(String、Number、Boolean、undefined、NaN…),或者对象(Map、Set、Object、Function、Symbol…),但是null不能作为key值。 - 通过
Map中的size属性,可以很方便地获取到Map长度,要获取Object的长度,只能手动计算。
3.4、总结
- 遍历顺序:
插入顺序。 - 对同一个键多次赋值,后面的值将覆盖前面的值。
- 对同一个对象的引用,被视为一个键。
- 对同样值的两个实例,被视为两个键。
- 键跟内存地址绑定,只要内存地址不一样就视为两个键。
- 添加多个以
NaN作为键时,只会存在一个以NaN作为键的值。 Object结构提供字符串—值的对应,Map结构提供值—值的对应。
4、WeakSet
WeakSet 结构与 Set 类似,也是不重复的值的集合,但成员值只能是对象。
4.1、介绍
- 成员都是数组和类似数组的对象,若调用
add()方法时传入了非数组和类似数组的对象的参数,就会抛出错误。 - 成员都是弱引用,可以被垃圾回收机制回收,可以用来保存
DOM节点,不容易造成内存泄漏。 WeakSet不可迭代,因此不能被用在for-of等循环中。- 入参具有
Iterator接口的数据结构。 WeakSet没有size属性。
const a = [1, 2, [1, 2]];
new WeakSet(a); // Uncaught TypeError: Invalid value used in weak set
weakSet 和 Set 的区别:
WeakSet只能是对象的集合,不像Set一样什么类型数据都能存储。WeakSet集合里面对对象的引用是弱引用,即如果集合里面的对象没有被外部引用,就会被回收掉。
4.2、方法
add(value):添加值,返回实例。delete(value):删除值,返回布尔。has(value):检查值,返回布尔。
const weakSet = new WeakSet();
const obj = {};
weakSet.add(obj); // WeakSet {{…}}
weakSet.has(obj); // true
weakSet.delete(obj); // true
4.3、应用场景
- 储存
DOM节点:DOM节点被移除时自动释放此成员,不用担心这些节点从文档移除时会引发内存泄漏。 - 临时存放一组对象或存放跟对象绑定的信息:只要这些对象在外部消失,它在
WeakSet结构中的引用就会自动消。
4.4、总结
- 成员都是
弱引用,垃圾回收机制不考虑WeakSet结构对此成员的引用。 - 成员不适合引用,它会随时消失,因此
ES6规定WeakSet结构不可遍历。 - 其他对象不再引用成员时,垃圾回收机制会自动回收此成员所占用的内存,不考虑此成员是否还存在于
WeakSet结构中。
5、WeakMap
WeakMap 结构与 Map 结构类似,也是用于生成键值对的集合,但是键必须是弱引用对象,而值可以是任意类型。
5.1、介绍
- 只接受对象作为键名(
null除外),不接受其他类型的值作为键名。 - 键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的。
- 入参具有
Iterator接口且每个成员都是一个双元素数组的数据结构。 - 不能遍历,方法有
get、set、has、delete。
WeakMap 和 Map 的区别:
WeakMap也是键值对集合,但是键必须是弱引用对象,而值可以是任意类型。WeakMap集合里面键的对象的引用是弱引用,即如果集合里面键的对象没有被外部引用,就会被回收掉。
const weakMap = new WeakMap();
let obj = {};
let obj2 = {};
let value = {n: 1};
weakMap.set(obj, 1); // WeakMap {{…} => 1}
weakMap.set(obj2, value); // WeakMap {{…} => {…}, {…} => 1}
weakMap.has(obj); // true
weakMap.get(obj); // 1
weakMap.has(obj2); // true
weakMap.get(obj2); // {n: 1}
obj = null;
weakMap.has(obj); // false
weakMap.get(obj); // undefined
value = null;
weakMap.has(obj2); // true
weakMap.get(obj2); // {n: 1}
weakMap.delete(obj2); // true
weakMap.has(obj2); // false
weakMap.get(obj2); // undefined
5.2、方法
set(key,value):添加键值对,返回实例。get(key): 返回键值对的值。has(key): 检查键值对,返回布尔。delete(key): 删除键值对,返回布尔。
5.3、应用场景
- 储存
DOM节点:DOM节点被移除时自动释放此成员键,不用担心这些节点从文档移除时会引发内存泄漏。 - 部署私有属性:内部属性是实例的弱引用,删除实例时它们也随之消失,不会造成内存泄漏。
5.4、总结
- 成员键都是
弱引用,垃圾回收机制不考虑WeakMap结构对此成员键的引用。 - 成员键不适合引用,它会随时消失,因此
ES6规定WeakMap结构不可遍历。 - 其他对象不再引用成员键时,垃圾回收机制会自动回收此成员所占用的内存,不考虑此成员是否还存在于
WeakMap结构中。 - 一旦不再需要,成员会自动消失,不用手动删除引用。
- 弱引用的
只是键而不是值,值依然是正常引用。 - 即使在外部消除了成员键的引用,内部的成员值依然存在。