set集合
set集合中存放的数据都是唯一的,不会发生重复
set集合内部使用Object.is()方法判断两个数据是否一致的,但有一种情况比较特殊,即set集合认为+0和-0是相等的,这两个数据不会使用Object.is()进行判断,而是直接认为两者是相等的
set集合是有“顺序”的,set集合中的数据会按照被添加的先后顺序依次进行排列
set集合是一个可迭代对象
创建set集合
创建一个没有任何内容的set集合
const set = new Set();
创建一个具有初始内容的set集合,初始内容为可迭代对象每次迭代所得到的结果
const set = new Set(iterable);
若Set构造函数中传入的是字符串,则函数内部会将该字符串转换为String对象,然后进行迭代(String对象是可迭代的)
const set = new Set("aaabbccc");
console.log(set); // Set(3) {"a", "b", "c"}
set集合的实例成员
-
size
set集合中的元素数量
该属性是只读的,不能被重新赋值
-
add(数据)
添加一个数据到set集合末尾,若添加的数据已经存在,则不进行任何操作
-
has(数据)
判断set集合中是否已经存在对应的数据
-
delete(数据)
删除set集合中相应的数据,返回是否删除成功
清空set集合
set集合与数组的相互转换
数组转为set
const set = new Set(arr);
set转为数组
const arr = [...set];
遍历set集合
使用for of循环
for(const item of set){
console.log(item);
}
使用set集合的实例方法forEach
set.forEach((item)=>{
console.log(item);
});
set的forEach中的回调函数能够接受三个参数,前两个参数都为当前遍历到的元素,第三个参数是set本身
const set = new Set(["a", "b", "c"]);
set.forEach((i1, i2, s)=>{
console.log(i1, i2, s);
});
/**
"a" "a" Set(3) {"a", "b", "c"}
"b" "b" Set(3) {"a", "b", "c"}
"c" "c" Set(3) {"a", "b", "c"}
*/
应用:求两个数组的并集、交集、差集
const arr1 = [1, 1, 2, 3, 4, 4];
const arr2 = [1, 3, 4, 4, 5, 6];
求并集:
// 两者组合起来就是并集
const arr3 = [...new Set([...arr1, ...arr2])];
// arr3 = [1, 2, 3, 4, 5, 6]
求交集:
// 两者都有的就是交集
const set1 = new Set(arr1);
const set2 = new Set(arr2);
const arr3 = [...set1].filter((item)=>{
return set2.has(item);
});
// arr3 = [1, 3, 4]
求差集:
// 并集中除开交集部分,剩下的就是差集
const set1 = new Set(arr1);
const set2 = new Set(arr2);
const set3 = new Set([...set1].filter((item)=>{
return set2.has(item);
}));
const arr3 = [...new Set([...arr1, ...arr2])].filter((item)=>{
return !set3.has(item);
});
// arr3 = [2, 5, 6]
手写set
JS的set实际上是使用C/C++代码写的,因此执行效率更高,这里只是模拟set的实现
class MySet {
constructor(iterable = []) {
// 验证iterator是否是可迭代对象
if (typeof iterable[Symbol.iterator] !== "function") {
throw TypeError(`${iterable} is not iterable`);
}
this._datas = [];
for (const item of iterable) {
this.add(item);
}
}
add(data) {
if (!this.has(data)) {
if (data === 0) {
this._datas.push(0);
} else {
this._datas.push(data);
}
}
}
has(data) {
for (const item of this._datas) {
if ((item === 0 && data === 0) || Object.is(item, data)) {
return true;
}
}
return false;
}
delete(data) {
for (let i = 0; i < this._datas.length; i++) {
const item = this._datas[i];
if ((item === 0 && data === 0) || Object.is(item, data)) {
this._datas.splice(i, 1);
return true;
}
}
return false;
}
clear() {
this._datas.length = 0;
}
*[Symbol.iterator]() {
for (const item of this._datas) {
yield item;
}
}
forEach(callback) {
for (const item of this._datas) {
callback(item, item, this);
}
}
get size() {
return this._datas.length;
}
}
map集合
map集合用于存储多个键值对数据
使用对象存储键值对存在以下问题:
- 键名只能是字符串或符号
- 获取数据的数量不方便
- 键名容易跟原型上的名称冲突
map中的键可以是任何类型,但也必须保证键唯一,map保证键唯一的方式与set保证数据唯一的方式相同
map集合是一个可迭代对象
map集合中键值对的顺序严格按照第一个加入该键值对的顺序排序
集合无法通过JSON.stringify()进行字符串序列化
创建map集合
创建一个空的map集合
const map = new Map();
创建一个具有初始内容的map集合,初始内容为可迭代对象每次迭代所得到的结果,但map要求该结果必须是一个长度为2的可迭代对象,对象的第一项表示键,对象的第二项表示值
const map = new Map(iterable);
const map = new Map([["a", 1], ["b", 2], ["c", 3]]);
console.log(map); // Map(3) {"a"=>1, "b"=>2, "c"=>3}
map集合的实例成员
-
size
map集合中键值对的数量
该属性是只读的,不能被重新赋值
-
set(key, value)
设置一个键值对,键和值可以是任何类型
如果键不存在,则添加一项到map中
如果键已存在,则修改键值对的值
-
get(key)
得到对应的键的键值对的值
如果不存在对应的键,则返回undefined
-
has(key)
判断是否存在对应的键
该方法只会判断map集合自身拥有的键
-
delete(key)
删除对应的键的键值对,返回是否删除成功
-
clear()
清空map集合
map集合与数组的相互转换
数组转为map
const map = new Map([["a", 1], ["b", 2], ["c", 3]]);
map转为数组
const arr = [...map];
map转换为的数组,其元素都是[key, value]的形式
const map = new Map([["a", 1], ["b", 2], ["c", 3]]);
const arr = [...map];
console.log(arr); // [["a", 1], ["b", 2], ["c", 3]]
遍历map集合
使用for of循环
每次循环得到的是一个[key, value]形式的数组
for(const item of map){
console.log(item);
}
使用map集合的实例方法forEach
map的forEach中的回调函数能够接受三个参数,第一个参数为每一项的值,第二个参数为每一项的键,第三个参数为map集合本身
const map = new Map([["a", 1], ["b", 2], ["c", 3]]);
map.forEach((value, key, m)=>{
console.log(value, key, m);
});
/**
1 "a" Map(3) {"a"=>1, "b"=>2, "c"=>3}
2 "b" Map(3) {"a"=>1, "b"=>2, "c"=>3}
3 "c" Map(3) {"a"=>1, "b"=>2, "c"=>3}
*/
手写map
JS的map实际上是使用C/C++代码写的,因此执行效率更高,这里只是模拟map的实现
class MyMap{
constructor(iterable = []){
// 验证iterator是否是可迭代对象
if (typeof iterable[Symbol.iterator] !== "function") {
throw TypeError(`${iterable} is not iterable`);
}
this._datas = [];
for(const item of iterable){
if (typeof item[Symbol.iterator] !== "function") {
throw TypeError(`${item} is not iterable`);
}
const iterator = item[Symbol.iterator]();
const key = iterator.next().value;
const value = iterator.next().value;
this.set(key, value);
}
}
set(key, value){
if(this.has(key)){
for(const item of this._datas){
if((key === 0 && item.key === 0) || Object.is(key, item.key)){
item.value = value;
break;
}
}
}else{
this._datas.push({key, value});
}
}
has(key){
for(const item of this._datas){
if((key === 0 && item.key === 0) || Object.is(key, item.key)){
return true;
}
}
return false;
}
get(key){
for(const item of this._datas){
if((key === 0 && item.key === 0) || Object.is(key, item.key)){
return item;
}
}
return undefined;
}
get size(){
return this._datas.length;
}
delete(key){
for(let i = 0; i < this._datas.length; i++){
const item = this._datas[i];
if((key === 0 && item.key === 0) || Object.is(key, item.key)){
this._datas.splice(i, 1);
return true;
}
}
return false;
}
clear() {
this._datas.length = 0;
}
*[Symbol.iterator]() {
for (const item of this._datas) {
yield [item.key, item.value];
}
}
forEach(callback){
for(const item of this._datas){
callback(item.value, item.key, this);
}
}
}
WeakSet和WeakMap
WeakSet
WeakSet和Set的功能基本相同,除了以下几个方面:
-
WeakSet中保存的引用不会影响垃圾回收
JS会定期回收一些无用的对象,这些对象是无法通过其他变量或属性找到的(即没有变量或属性保存该对象的地址),这些对象称之为垃圾
垃圾回收时,会考虑普通set
let obj = { a: 1 }; const set = new Set(); set.add(obj); obj = null; console.log(set);如上set中保存了该对象的引用,因此尽管obj已经不指向该对象了,该对象也仍然不会被当做垃圾回收掉
但垃圾回收是不会考虑WeakSet的
let obj = { a: 1 }; const set = new WeakSet(); set.add(obj); obj = null; console.log(set);尽管WeakSet中还保存有对象的引用,但垃圾回收不考虑WeakSet,因此对象最终还是被回收掉了
-
WeakSet只能添加对象
-
WeakSet是不可迭代的
-
WeakSet没有size属性,没有forEach方法
WeakMap
WeakMap和Map的功能基本相同,除了以下几个方面:
-
WeakMap的键所保存的引用不会影响垃圾回收
当某个对象只被WeakMap中的一个键所引用时,该键所属的键值对最终就都会被当作垃圾回收掉
let obj = { a: 1 }; const map = WeakMap(); map.set(obj, "abcabc"); obj = null; console.log(map); -
WeakMap的键必须是对象
-
WeakMap是不可迭代的
-
WeakMap没有size属性,没有forEach方法