浅谈JS的映射和集合

本文最后更新于:9 个月前

目录

引言

Map

特点

属性及方法

创建

新增

获取

是否存在值

删除

清空

获取长度

键的迭代器

值的迭代器

键值对的迭代器

遍历

使用场景

描述映射关系

缓存

Set

特点

属性及方法

创建及初始化

新增

删除

其他属性

使用场景

数组去重

集合操作

WeakMap

特点

属性及方法

创建及初始化

其他属性

使用场景

属性私有化

避免内存泄漏

WeakSet

特点

属性及方法

创建及初始化

其他属性

使用场景

避免内存泄漏

总结


引言

Map(映射)和Set(集合)是ES6引入的数据结构,它们提供了更灵活、高效的方式来存储和访问数据。本文将介绍一下这两种数据结构以及WeakMap(弱映射)和WeakSet(弱集合)这两种新的数据结构的概念及使用。

Map

Map是一种以键值对的形式存储数据的集合。它的结构类似于对象,它也是键值对的集合,但是Map的键可以是任何类型的值,包括对象、函数和原始类型等。可以说Map就是对象的一种扩展。

特点

  1. 键是唯一的,后添加的键值对会替换前面的
  2. 键值是有序的,使用Map对象将按照插入顺序得到每一个键值对
  3. 各种类型的值都可以当作键

属性及方法

创建

我们可以通过下面的代码创建一个空的Map

const map = new Map()
console.log(map); // Map(0) {}

赋予初始值:map使用 [ ] 表示层级结构,第一维度是映射的个数,第二维度是具体的值,结构是 [key, value]

const map = new Map<string, string | number>([['name', '阿黄'], ['age', 20]])
console.log(map); // Map(2) { 'name' => '阿黄', 'age' => 20 }

新增

通过set(key, value)来增加一个键值对,如果键已存在,则会更新对应的值

const map = new Map()
map.set('name', '阿黄')
console.log(map);// Map(1) { 'name' => '阿黄' }
map.set('name', '小黑')
console.log(map);// Map(1) { 'name' => '小黑' }

获取

使用get(key)返回key对应的值,如果值不存在则返回undefined

const map = new Map()
console.log(map.get('name')); // undefined
map.set('name', '阿黄')
console.log(map.get('name')); // 阿黄

是否存在值

使用map.has(key)可以判断是否存在键对应值

const map = new Map()
console.log(map.has('name')); // false
map.set('name', '阿黄')
console.log(map.has('name')); // true

删除

通过delete(key)来删除map的值

const map = new Map()
map.set('name', '阿黄')
console.log(map); // Map(1) { 'name' => '阿黄' }
map.delete('name')
console.log(map); // Map(0) {}

清空

使用map.clear()函数清空map

const map = new Map()
map.set('name', '阿黄')
map.set('age', 12)
console.log(map); // Map(2) { 'name' => '阿黄', 'age' => 12 }
map.clear()
console.log(map); // Map(0) {}

获取长度

map可以通过size获取子集个数

const map = new Map()
map.set('name', '阿黄')
map.set('age', 12)
console.log(map.size); // 2

键的迭代器

使用keys函数可以返回Map中所有的键的迭代器

const map = new Map()
map.set('name', '阿黄')
map.set('age', 12)
console.log(map.keys()); // { 'name', 'age' }

值的迭代器

和keys类似,values函数可以返回Map中所有的值的迭代器

const map = new Map()
map.set('name', '阿黄')
map.set('age', 12)
console.log(map.values()); //  { '阿黄', 12 }

键值对的迭代器

使用entries或者是Symbol.iterator函数可以返回Map的键值对

const map = new Map()
map.set('name', '阿黄')
map.set('age', 12)
console.log(map.entries()); // { [ 'name', '阿黄' ], [ 'age', 12 ] }
console.log(map[Symbol.iterator]()); // { [ 'name', '阿黄' ], [ 'age', 12 ] }

遍历

使用forEach函数进行遍历,和数组的forEach类似(数组第二项是索引),callback函数返回三个值,分别是value,key,map

const map = new Map()
map.set('name', '阿黄')
map.set('age', 12)
map.forEach((val, key, map) => {
    console.log(val, key, map);
})
// 阿黄 name Map(2) { 'name' => '阿黄', 'age' => 12 }
// 12 age Map(2) { 'name' => '阿黄', 'age' => 12 }

使用场景

描述映射关系

在对象中我们只能使用string或symbol来描述键的值,而Map可以很好的描述关系映射

const map = new Map()
map.set(['meat', 'fish'], '都是阿黄爱吃的')
console.log(map); // Map(1) { [ 'meat', 'fish' ] => '都是阿黄爱吃的' }

缓存

我们使用Map的索引唯一性特点将其运用于缓存操作,下面的代码中我们使用一个函数作为map的键,第一次调用函数会触发set操作,后续操作会直接使用get获取对应值

const cache = new Map();
const temp = (key: any) => cache.get(key) ?? (cache.set(key, getNum(1, 2)), cache.get(key))// 缓存操作,首次访问会执行set操作
const getNum = (a: number, b: number) => a + b
console.log(temp(getNum))
console.log(temp(getNum))

Set

Set是一种无序且唯一的集合(可以当成一个数学集合),它可以存储任何类型的值,包括对象、函数和原始类型等。它的结构类似数组,但是其中的值不允许重复,这点与Map的键值类似。

特点

  1. 成员的值都是唯一的
  2. 值的无序性(数据存储不依赖索引,而是哈希表)

属性及方法

创建及初始化

const set = new Set<string>()
console.log(set);// Set(0) {}

初始化可以使用[ value ]或任意数组进行赋值

const set = new Set<string>(['阿黄'])
console.log(set);// Set(1) { '阿黄' }

新增

Set的新增子项与Map不太一样,可以使用add函数进行操作

const set = new Set(['阿黄'])
set.add('小黑')
console.log(set);// Set(2) { '阿黄', '小黑' }

删除

set使用delete对集合的单项进行删除

const set = new Set<string>(['阿黄'])
set.delete('阿黄')
console.log(set);// Set(0) {}

其他属性

剩下的属性与Map几乎相同,用法上就不做赘述。此外,Set的迭代器方法比较特殊,使用set.values()和set.keys()的结果相同,因为Set的key和value都是value值

const set = new Set<string>(['阿黄'])
console.log(set.values()); // [Set Iterator] { '阿黄' }
console.log(set.keys()); // [Set Iterator] { '阿黄' }
console.log(set.entries()); // [Set Entries] { ['阿黄', '阿黄'] }

使用场景

数组去重

const set = new Set([6, 6, 4, 2, 3, 4, 3, 3, 1, 5, 5, 2]);
console.log(set);// Set(6) { 6, 4, 2, 3, 1, 5 }

集合操作

交集,并集,差集

const set = new Set([1, 2, 3, 4, 5]);
const set2 = new Set([1, 3, 5, 7, 9]);
// 交集
console.log([...set].filter(it => set2.has(it)));
// 并集
console.log(new Set([...set, ...set2]));
// 差集
console.log([...set].filter(it => !set2.has(it)));

WeakMap

在了解WeakMap之前,先看段代码

let obj: any = {
    name: "阿黄"
}
const map = new Map([[obj, "阿黄"]]);
obj = null
console.log(map);// Map(1) { { name: '阿黄' } => '阿黄' }

上面的代码中我们新建了一个object类型的变量,使其作为键赋值到Map中,当我们把obj删除时,Map中的键值没有发生变化,说明其二者是深复制关系,此时的obj存在与Map中不会被垃圾回收机制处理。此时如果我们想清除这个obj在Map中的引用对象,就可以使用WeakMap。

回到正题,WeakMap是一种弱引用的哈希表对象,就像上面的例子,如果键对象不再被引用,它将被垃圾回收并从WeakMap中自动删除,从而避免了内存泄漏的问题

特点

  1. 键名只能是对象
  2. 键名使用弱引用

属性及方法

创建及初始化

这与Map相似,只不过键名只能传对象类型的

let obj: any = {
    name: "阿黄"
}
const weakMap = new WeakMap([[obj, "阿黄"]]);

其他属性

WeakMap只拥有Map的get、set、has、delete这几个方法,不包含iterator迭代属性,也就是说clear需要手动一个一个删除

const obj = {
    name: "阿黄"
}
const obj2 = {
    name: "小黑"
}
const weakMap = new WeakMap([[obj, "阿黄"]]);
weakMap.set(obj2, "小黑")
weakMap.delete(obj)
console.log(weakMap.has(obj)); // false
console.log(weakMap.get(obj2)); // 小黑

使用场景

属性私有化

使用WeakMap可以对类的属性进行封装

const animalPrivate = new WeakMap()
class Animal {
    constructor() {
        animalPrivate.set(this, {
            name: "阿黄"
        })
    }
    getName() {
        return animalPrivate.get(this).name
    }
}

console.log(new Animal().getName());

避免内存泄漏

与Map效果不同,关联的object删除后weakMap键名也会删除

let obj: any = {
    name: "阿黄"
}
const weakMap = new WeakMap([[obj, "阿黄"]]);
console.log(weakMap.get(obj)); // 阿黄
obj = null
console.log(weakMap.get(obj)); // undefined

WeakSet

WeakSet和WeakMap一样,也可以避免内存泄漏,它的每一项只能传对象类型

特点

  1. 值只能是对象
  2. 值使用弱引用

属性及方法

创建及初始化

const obj = {
    name: "阿黄"
}
const weakSet = new WeakSet([obj]);

其他属性

WeakSet也是Set的简化版,只有add,delete,has三个属性,和WeakMap一样不支持iterator属性,无法迭代

const obj = {
    name: "阿黄"
}
const obj2 = {
    name: "小黑"
}
const weakSet = new WeakSet([obj]);
weakSet.add(obj2)
weakSet.delete(obj)
console.log(weakSet.has(obj));// false

使用场景

避免内存泄漏

这点和WeakMap一样

let obj: any = {
    name: "阿黄"
}
const weakSet = new WeakSet([obj]);
obj = null
console.log(weakSet.has(obj));// false

总结

本文主要介绍了ES6引入的四种新的数据结构,分别是Map(映射)、Set(集合)、WeakMap(弱映射)和WeakSet(弱集合)。Map键可以是任意类型,唯一且有序;Set键值合一并可以是任意值,唯一且无序;WeakMap是键限制为对象的青春版Map;WeakSet是值为对象的迷你版Set,Weak的二者的键被垃圾回收后即刻消失。

以上就是文章全部内容,如果文章有帮到你或觉得文章不错的,还请支持一下作者,谢谢!