Set 集合
类似于数组 Array,但 Set 里面不能有重复值、且 Set 元素的 [下标] & [值] 是一样的
let arr = ['a', 'b', 'a'];
console.log(arr); // [ 'a', 'b', 'a' ]
let set = new Set(arr);
console.log(set); // Set { 'a', 'b' }
初始化实例
- 语法:
new Set(arr)
,接收 1 个数组参数
let set = new Set(['a', 'b', 'c']);
console.log(set); // Set { 'a', 'b', 'c' }
- 可以通过 Set 实例的
size
属性获取集合的长度
let set = new Set(['a', 'b', 'c']);
console.log(set.size); // 3
常用方法
set.add(XXX)
:添加元素
- 只接收 1 个参数,后面的参数会被忽略
let set = new Set();
set.add('a', 'c');
console.log(set); // Set { 'a' }
- 执行
add()
返回 Set 实例自身,所以可以 [链式调用]
let set = new Set();
set.add('a').add('b').add('c');
console.log(set); // Set { 'a', 'b', 'c' }
set.delete(XXX)
:删除元素
- 只接收 1 个参数,后面的参数会被忽略
- 返回
true
-删除成功、false
-删除失败
let set = new Set(['a', 'b', 'c']);
set.delete('a', 'b');
console.log(set); // Set { 'b', 'c' }
set.has(XXX)
:判断有没有指定值
- 返回
true
-有、false
-无
let set = new Set(['a', 'b', 'c']);
console.log(set.has('a')); // true
console.log(set.has('s')); // false
set.clear()
:清空内容
- 不需要参数、没有返回值
let set = new Set(['a', 'b', 'c']);
console.log(set); // Set { 'a', 'b', 'c' }
set.clear();
console.log(set) // Set {}
遍历元素
let set = new Set(['a', 'b', 'c']);
forEach()
:
set.forEach((value, index) => {
console.log(value, index); // a a b b c c
});
for … of
:
for (let item of set) { // 默认获取 values
console.log(item); // a b c
}
for (let item of set.keys()) { // 使用 set.keys() → 遍历 key
console.log(item); // a b c
}
for (let item of set.values()) { // 使用 set.values() → 遍历 value
console.log(item); // a b c
}
for (let item of set.entries()) { // 使用 set.entries() → 遍历 [键值对]
console.log(item); // [ 'a', 'a' ] [ 'b', 'b' ] [ 'c', 'c' ]
}
for (let [key, value] of set.entries()) { // 解构赋值 + set.entries() → 分别获取 [键值对]
console.log(key, value); // a a b b c c
}
- 可以发现,set 的 [下标] & [值] 是一样的
实用操作
集合 → 数组
转为数组后,能使用数组身上 set 不具有的一些方法,比如 map
① 使用扩展运算符 ...
let arr = [...new Set()];
let [...arr] = new Set();
② 使用方法 Array.from()
let arr = Array.from(new Set(['a', 'b', 'b'])); // ["a", "b"]
[数组] / [字符串] 去重
let arr = [1, 3, 5, 1, 2, 6, 3, 1, 5, 2]; // 数组
let newArr = [...new Set(arr)];
console.log(newArr); // [1, 3, 5, 2, 6]
let str = 'abcddbca'; // [字符串] 作为参数
let newStr = [...new Set(str)].join('');
console.log(newStr); // abcd
注意:
- Set 内部判断两个值是否不同,使用的算法叫做 “Same-value equality”,它类似于精确相等运算符
===
;
主要区别是:精确相等运算符认为NaN != NaN
,但前者认为NaN === NaN
- 两个对象总是不相等的!!!
let arr = ['a', 'b', 'c'];
let set1 = new Set(arr);
let set2 = new Set(arr);
console.log(set1 == set2); // false
WeakSet
- 弱集合 WeakSet 中的元素只能是 [Object] / [继承 Object 的] 类型
- 但是,如果初始化时直接放入 [对象] 作为参数,又会报错;
最好是:创建空 WeakSet 实例,然后用add()
添加
let wSet = new WeakSet();
let obj = { name: "superman" };
wSet.add(obj);
console.log(wSet); // WeakSet {{…}}
WeakSet & Set
- WeakSet 实例没有
size
属性 - WeakSet 实例的
add(XXX)
、delete(XXX)
、has(XXX)
方法,需要传入 [对象] 作为参数 - WeakSet 实例不可迭代,不能使用
for … of
、forEach
循环,也不能使用keys()
、values()
、entries()
方法
Map 映射
类似于对象,但是对象的 key 只能是字符串;map 的 key 可以为任意类型
就是说,Object 提供了 [字符串 ↔ 值] 的对应,Map 提供了 [值 ↔ 值] 的对应,是一种更完善的 Hash 结构
初始化实例
new Map([[key1, value1], [key2, value2], ...])
const map = new Map([['name', 'superman'], ['gender', 'male']]);
console.log(map); // Map { 'name' => 'superman', 'gender' => 'male' }
- 可以通过
size
属性获取映射中的键值对的数量
let map = new Map([['name', 'superman']]);
console.log(map.size); // 1
常用方法
map.set(key, value)
:添加 key-value
- 返回 map 自身,所以可以 [链式调用]
let map = new Map();
map.set('name', 'superman').set("age", 21)
console.log(map); // Map { 'name' => 'superman', 'age' => 21 }
map.get(key)
:获取指定 key 对应的 value
- 查询成功则返回 value、否则返回
undefined
let map = new Map();
let obj = {};
map.set(obj, 'superman'); // key 为 [对象]
console.log(map.get(obj)); // superman
map.has(key)
:查看是否存在指定 key-value
- 返回
true
-存在、false
-不存在
let map = new Map();
console.log(map.has("name")); // false
map.delete(key)
:删除指定 key-value
- 删除成功返回
true
、否则返回false
let map = new Map();
let arr = [];
map.set(arr, 'superman'); // key 为 [数组]
console.log(map); // Map { [] => 'superman' }
map.delete(arr);
console.log(map); // Map {}
map.clear()
:清空映射
- 无参数、无返回值
let map = new Map();
let fun = () => { };
map.set(fun, 'superman'); // key 为 [函数]
console.log(map); // Map { [Function: fun] => 'superman' }
map.delete(fun);
console.log(map); // Map {}
遍历属性
const map = new Map([['name', 'superman'], ['gender', 'male']]);
forEach()
map.forEach((item, index) => {
console.log(item, index); // superman name male gender
});
for … of
for (const item of map) { // 默认获取 key-value
console.log(item); // [ 'name', 'superman' ] [ 'gender', 'male' ]
}
for (const item of map.keys()) { // 使用 map.keys() → 遍历 key
console.log(item); // name gender
}
for (const item of map.values()) { // 使用 map.values() → 遍历 value
console.log(item); // superman male
}
for (const item of map.entries()) { // 使用 map.entries() → 遍历 [键值对]
console.log(item); // [ 'name', 'superman' ] [ 'gender', 'male' ]
}
Object & Map
- 内存占用
给定固定大小的内存,Map 比 Object 多存储约 50% 的键值对 - 插入性能
插入 Map 在所有浏览器中一般会稍微快一点 - 查找速度
键值对较多的话,二者性能差异极小,或许 Object 稍快一些 - 删除性能
使用delete
删除 Object 属性的性能较差,为此出现了一些伪删除操作,包括把属性值设置为 undefined / null
对大多数浏览器引擎来说,Map 的delete()
操作更快。如果代码涉及大量删除操作,应选择 Map
WeakMap
- WeakMap 是弱映射
- WeakMap 实例的 key 必须是 [Object] / [继承 Object 的] 类型
如果 key 不是 [对象],就会报错
let obj = {};
let wMap = new WeakMap([[obj, 'superman']]);
console.log(wMap); // WeakMap {{…} => 'superman'}
console.log(wMap.get(obj)); // superman