map/set
之前在学习 ES6 的时候,看到
Set
和
Map
,不知道其应用场景有哪些,只觉得很多时候会用在数组去重和数据存储,后来慢慢才领悟到
Set
是一种叫做集合的数据结构,
Map
是一种叫做字典的数据结构。
Set
本身是一个构造函数,用来生成
Set
数据结构。
Set
函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。
Set
对象允许你存储任何类型的值,无论是原始值或者是对象引用。它类似于数组,但是成员的值都是唯一的,没有重复的值。
const s = new Set()
[2, 3, 5, 4, 5, 2, 2].forEach((x) => s.add(x))
for (let i of s) {
console.log(i)
// 2 3 5 4
Set 中的特殊值
Set
对象存储的值总是唯一的,所以需要判断两个值是否恒等。有几个特殊值需要特殊对待:
+0 与 -0 在存储判断唯一性的时候是恒等的,所以不重复
undefined
与 undefined
是恒等的,所以不重复
NaN
与 NaN
是不恒等的,但是在 Set
中认为 NaN
与 NaN
相等,所有只能存在一个,不重复。
Set 的属性:
size
:返回集合所包含元素的数量
const items = new Set([1, 2, 3, 4, 5, 5, 5, 5])
items.size // 5
Set 实例对象的方法
add(value)
:添加某个值,返回 Set
结构本身(可以链式调用)。
delete(value)
:删除某个值,删除成功返回 true
,否则返回 false
。
has(value)
:返回一个布尔值,表示该值是否为 Set
的成员。
clear()
:清除所有成员,没有返回值。
s.add(1).add(2).add(2)
// 注意2被加入了两次
s.size // 2
s.has(1) // true
s.has(2) // true
s.has(3) // false
s.delete(2)
s.has(2) // false
keys()
:返回键名的遍历器。
values()
:返回键值的遍历器。
entries()
:返回键值对的遍历器。
forEach()
:使用回调函数遍历每个成员。
由于 Set
结构没有键名,只有键值(或者说键名和键值是同一个值),所以 keys
方法和 values
方法的行为完全一致。
let set = new Set(['red', 'green', 'blue'])
for (let item of set.keys()) {
console.log(item)
// red
// green
// blue
for (let item of set.values()) {
console.log(item)
// red
// green
// blue
for (let item of set.entries()) {
console.log(item)
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
Array 和 Set 对比
Array
的 indexOf
方法比 Set
的 has
方法效率低下
Set
不含有重复值(可以利用这个特性实现对一个数组的去重)
Set
通过 delete
方法删除某个值,而 Array
只能通过 splice
。两者的使用方便程度前者更优
Array
的很多新方法 map
、filter
、some
、every
等是 Set
没有的(但是通过两者可以互相转换来使用)
Set 的应用
1、Array.from
方法可以将 Set
结构转为数组。
const items = new Set([1, 2, 3, 4, 5])
const array = Array.from(items)
2、数组去重
// 去除数组的重复成员
;[...new Set(array)]
Array.from(new Set(array))
3、数组的 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}
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}
weakSet
WeakSet
结构与 Set
类似,也是不重复的值的集合。
成员都是数组和类似数组的对象,若调用 add()
方法时传入了非数组和类似数组的对象的参数,就会抛出错误。
const b = [1, 2, [1, 2]]
new WeakSet(b) // Uncaught TypeError: Invalid value used in weak set
成员都是弱引用,可以被垃圾回收机制回收,可以用来保存 DOM 节点,不容易造成内存泄漏。
WeakSet
不可迭代,因此不能被用在 for-of
等循环中。
WeakSet
没有 size
属性。
Map
中存储的是 key-value
形式的键值对, 其中的 key
和 value
可以是任何类型的, 即对象也可以作为 key
。 Map
的出现,就是让各种类型的值都可以当作键。Map
提供的是 “值-值”的对应。
Map 和 Object 的区别
Object
对象有原型, 也就是说他有默认的 key
值在对象上面, 除非我们使用 Object.create(null)
创建一个没有原型的对象;
在 Object
对象中, 只能把 String
和 Symbol
作为 key
值, 但是在 Map
中,key
值可以是任何基本类型(String
, Number
, Boolean
, undefined
, NaN
….),或者对象(Map
, Set
, Object
, Function
, Symbol
, null
….);
通过 Map
中的 size
属性, 可以很方便地获取到 Map
长度, 要获取 Object
的长度, 你只能手动计算
Map 的属性
size: 返回集合所包含元素的数量
const map = new Map()
map.set('foo', ture)
map.set('bar', false)
map.size // 2
Map 对象的方法
set(key, val)
: 向 Map
中添加新元素
get(key)
: 通过键值查找特定的数值并返回
has(key)
: 判断 Map
对象中是否有 Key
所对应的值,有返回 true
,否则返回 false
delete(key)
: 通过键值从 Map
中移除对应的数据
clear()
: 将这个 Map
中的所有元素删除
const m = new Map()
const o = { p: 'Hello World' }
m.set(o, 'content')
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false
keys()
:返回键名的遍历器
values()
:返回键值的遍历器
entries()
:返回键值对的遍历器
forEach()
:使用回调函数遍历每个成员
const map = new Map([
['a', 1],
['b', 2],
for (let key of map.keys()) {
console.log(key)
// "a"
// "b"
for (let value of map.values()) {
console.log(value)
for (let item of map.entries()) {
console.log(item)
// ["a", 1]
// ["b", 2]
// 或者
for (let [key, value] of map.entries()) {
console.log(key, value)
// "a" 1
// "b" 2
// for...of...遍历map等同于使用map.entries()
for (let [key, value] of map) {
console.log(key, value)
// "a" 1
// "b" 2
数据类型转化
Map 转为数组
let map = new Map()
let arr = [...map]
数组转为 Map
Map: map = new Map(arr)
Map 转为对象
let obj = {}
for (let [k, v] of map) {
obj[k] = v
对象转为 Map
for( let k of Object.keys(obj)){
map.set(k,obj[k])
Map的应用
在一些 Admin 项目中我们通常都对个人信息进行展示,比如将如下信息展示到页面上。传统方法如下。
<div class="info-item">
<span>姓名</span>
<span>{{info.name}}</span>
</div>
<div class="info-item">
<span>年龄</span>
<span>{{info.age}}</span>
</div>
<div class="info-item">
<span>性别</span>
<span>{{info.sex}}</span>
</div>
<div class="info-item">
<span>手机号</span>
<span>{{info.phone}}</span>
</div>
<div class="info-item">
<span>家庭住址</span>
<span>{{info.address}}</span>
</div>
<div class="info-item">
<span>家庭住址</span>
<span>{{info.duty}}</span>
</div>
js 代码
mounted() {
this.info = {
name: 'jack',
sex: '男',
age: '28',
phone: '13888888888',
address: '广东省广州市',
duty: '总经理'
我们通过 Map 来改造,将我们需要显示的 label 和 value 存到我们的 Map 后渲染到页面,这样减少了大量的html代码
<template>
<div id="app">
<div class="info-item" v-for="[label, value] in infoMap" :key="value">
<span>{{label}}</span>
<span>{{value}}</span>
</div>
</div>
</template>
js 代码
data: () => ({
info: {},
infoMap: {}
mounted () {
this.info = {
name: 'jack',
sex: '男',
age: '28',
phone: '13888888888',
address: '广东省广州市',
duty: '总经理'
const mapKeys = ['姓名', '性别', '年龄', '电话', '家庭地址', '身份']
const result = new Map()
let i = 0
for (const key in this.info) {
result.set(mapKeys[i], this.info[key])
this.infoMap = result
WeakMap
WeakMap
结构与 Map
结构类似,也是用于生成键值对的集合。
只接受对象作为键名(null
除外),不接受其他类型的值作为键名
键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的
不能遍历,方法有 get
、set
、has
、delete
是一种叫做集合的数据结构(ES6新增的)
成员唯一、无序且不重复
[value, value]
,键值与键名是一致的(或者说只有键值,没有键名)
允许储存任何类型的唯一值,无论是原始值或者是对象引用
可以遍历,方法有:add
、delete
、has
、clear
WeakSet
成员都是对象
成员都是弱引用,可以被垃圾回收机制回收,可以用来保存 DOM
节点,不容易造成内存泄漏
不能遍历,方法有 add
、delete
、has
是一种类似于字典的数据结构,本质上是键值对的集合
可以遍历,可以跟各种数据格式转换
操作方法有:set
、get
、has
、delete
、clear
WeakMap
只接受对象作为键名(null
除外),不接受其他类型的值作为键名
键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的
不能遍历,方法有 get
、set
、has
、delete
兔子零84
管理员丨全栈工程师