Set和Map数据结构
Set
Set是ES6提供的数据结构,类似于数组,但是成员的值是唯一的,木有重复。Set本身是一个构造函数
ps:构造函数可以创建多个对象的实例,开头字母要大写,this指向实例,使用new来生成实例对象
基本用法
1 | const s = new Set() |
add方法:向Set结构加入成员
1 | [1,2,3,3].forEach(x=>s.add(x)) |
Set可以接受一个数组(或者具有iterable接口的其他数据结构)作为参数,用来初始化。如:
1 | const set = new Set([1,2,3,4,4]) |
获取Set实例长度
1 | const item = new Set([1,2,3,4,4]) |
可以利用Set去除数组的重复成员,
1 | [...new Set(array)] |
Array.from方法可以将Set结构转换为数组
1 | function dedupe(array){ |
向Set加入值,不会使值发生类型转换,如5和”5”是两个不一样的值,Set内容判断是否相等的算法认为NaN等于本身,这点和===不一样。
另外,两个对象是不相等的
1 | let set = new Set() |
Set实例属性和方法
- add(val): 添加某个值, 返回Set结构本身
- delete(val):删除某个值,返回一个布尔值,表示是否删除成功
- has(val): 返回一个布尔值,表示参数是否为Set成员
- clear():清除所有成员,没有返回值
1
2
3
4
5let set = new Set()
set.add(1).add(2)
set.has(1) // true
set.delete(2) // true
set.has(2) // false
遍历操作
Set结构的实例有4种遍历方法:
- keys(): 返回键名的遍历器
- values(): 返回键值的遍历器
- entries(): 返回键值对的遍历器
- forEach():使用回调函数遍历每个成员
Set结构没有键名,只有键值(键名等于键值),所以keys和values方法行为是一样的Set实例默认可遍历,默认遍历器生成的函数就是values方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let set = new Set(['green','red','yellow'])
for(let item of set.keys()){
console.log(item)
// green
// red
// yellow
}
for(let item of set.values()){
console.log(item)
// green
// red
// yellow
}
for(let item of set.entries()){
console.log("entries",item)
// entries [ 'green', 'green' ]
// entries [ 'red', 'red' ]
// entries [ 'yellow', 'yellow' ]
}这意味着可以使用for…of代替values遍历Set1
2Set.prototype[Symbol.iterator] === Set.prototype.values
// trueforEach方法用于对每个成员执行某些操作,没有返回值1
2
3
4
5
6
7let set = new Set(['red','green','yellow'])
for(let x of set){
console.log(x)
}
// red
// green
// yellowforEach方法的参数是一个处理函数,该函数的参数依次是键值、键名、集合本身;另外,forEach方法还可以有第二个参数,表示绑定的this对象1
2
3
4
5
6
7let set = new Set([1,2,3])
set.forEach((val,key)=>{
console.log(val*2)
})
// 2
// 4
// 6 - 遍历的应用*
数组的map和filter也可以用于Setfilter适合应用于求交集、并集、差集1
2
3
4let set = new Set([1,2,3])
console.log([...set]) // [1, 2, 3]
set = new Set([...set].map(x=>x*2))
console.log(set) // Set:{2, 4, 6}WeakSet
WeakSet也是不重复值的集合,但是与Set有两个区别
- WeakSet的成员只能是对象
- WeakSet中的对象都是弱引用(即垃圾回收机制不管WeakSet还用不用这个对象,只要其他对象不引用这个对象,该对象就被回收了)
WeakSet不存在内存泄漏的问题,所以适合临时存放一组对象
ES6规定WeakSet不可遍历。
WeakSet有add,delete,has方法, 但是没有size属性和forEach方法
Map
JS的对象(Object)本质上是键值对的集合(hash结构),但是只能用字符作为键。
为了解决这个问题,ES6提供了Map数据结构。
含义
Map类似于对象,也是键值对的集合,但是键的类型可以是各种类型的值。Map比Object更适合“键值对”的数据结构。
简单应用
1 | const m = new Map() |
Map也可以接受一个数组作为参数
1 | const map = new Map([['name','John'],['title','Author']]) |
错误示范,外层数组漏加[]
1 | const map = new Map(['name','John'],['title','Author']) |
除了数组,任何具有Iterator接口且每个成员都是一个双元素数组的数据结构都可以当作Map构造函数的参数。即Set和Map也可以作为Map构造函数的参数。
1 | const set = new Set([['foo', 1], ['bar', 2]]) |
如果对同一个键多次赋值,后面的值会覆盖前面的值
1 | const map = new Map() |
如果读取一个未知的键,则返回undefined
1 | new Map().get('aaa') // undefined |
注意,只有对同一个对象的引用,Map结构才将其视为同一个键
1 | const map = new Map() |
1 | const map = new Map() |
同理,同样的值的两个实例在Map结构中也被视为两个键。
1 | const map = new Map() |
由此可知,Map的键是和内存地址绑定的。
如果Map的键是一个简单数据类型的值(数字、字符串、布尔值),只要两个值严格相等(包括-0和+0)则为一个键。NaN虽然不严格相等,但是Map会把它视为一个键。
属性
size:返回Map结构的成员总数
方法
- set(key, value): 设置key所对应的键值,然后返回整个Map结构。
set方法返回的是当前的Map对象,所以可以采用链式写法。1
2const map = new Map()
map.set(1, 'a').set(2, 'b').set(3, 'c') - get(key): 读取key对应的键值,如果找不到key就会返回undefined
- has(key): 返回一个布尔值,表示某个键是否在Map数据结构中存在。
- delete:删除某个键,返回true,如果删除失败,则返回false。
- clear(): 清除所有成员,没有返回值。
遍历方法
Map原生提供了3个遍历器生成函数和1个遍历方法。
- keys(): 返回键名的遍历器
- values(): 返回键值的遍历器
- entries(): 返回所有成员的遍历器
- forEach(): 遍历Map的所有成员。
需要注意的是,Map的遍历顺序就是插入排序,跟Set一样
Map结构默认的遍历器接口是entries方法。这意味着可以使用for…of代替entries方法。
Map结构转为数组结构的比较快速的方法就是结合扩展运算符(…)
1 | const map = new Map([['aaa',111],['bbb',222],['ccc', 3]]); |
结合数组的map方法、filter方法,可以实现Map的遍历和过滤(Map本身么有map和filter方法)。
1 | const map = new Map([['aaa',111],['bbb',222],['ccc', 3]]); |
与其他数据结构的相互转换
Map转为数组
Map转为数组最方便的方法就是使用扩展运算符(…)。
1 | const myMap = new Map() |
数组转为Map
将数组传入Map构造函数就可以转为Map。
1 | const map = new Map([ |
Map转为对象
如果Map的所有键都是字符串,就可以转为对象。
1 | function strMapToObj(strMap){ |
对象转为Map
1 | function strObjToMap(obj){ |
Map转为JSON
Map转为JSON有两种情况:
- Map的键名都是字符串,这时可以转为JSON对象
1
2
3
4
5
6
7
8
9
10
11
12
13function strMapToObj(strMap){
let obj = Object.create(null)
for(let [k,v] of strMap){
obj[k] = v
}
return obj
}
function strMapToJson(strMap){
return JSON.stringify(strMapToObj(strMap))
}
const myMap = new Map().set('yes',true).set('no',false)
console.log(strMapToJson(myMap))
// {"yes":true,"no":false} - Map的键名非字符串,可以选择转为数组JSON
1
2
3
4
5
6function strMapToArrayJson(strMap){
return JSON.stringify([...strMap])
}
const myMap = new Map().set(true,7).set({foo:3},['abc'])
console.log(strMapToArrayJson(myMap))
// [[true,7],[{"foo":3},["abc"]]]
JSON转为Map
也分为两种情况
- 所有键名都是字符串
1
2
3
4
5
6
7
8
9
10
11
12function strObjToMap(obj){
let strMap = new Map()
for(let k of Object.keys(obj)){
strMap.set(k,obj[k])
}
return strMap
}
function jsonToStrMap(jsonStr){
return strObjToMap(JSON.parse(jsonStr))
}
console.log(jsonToStrMap('{"yes":true,"no":false}'))
// Map { 'yes' => true, 'no' => false } - JSON本身是一个数组,且数组成员本身又是具有两个成员的数组。它可以一一对应转为Map。注意JSON键名不要用’,不然报错SyntaxError: Unexpected token ‘ in JSON
1
2
3
4
5
6function jsonToMap(jsonStr){
return new Map(JSON.parse(jsonStr))
}
const myJson = '[[true,7],[{"foo":3},["abc"]]]'
console.log(jsonToMap(myJson))
// Map { true => 7, { foo: 3 } => [ 'abc' ] }
WeakMap
WeakMap结构与Map结构类似,也用于生成键值对的集合。
但是跟Map有两点区别
- WeakMap只能接受对象作为键名(null除外)
- WeakMap的键名所指向的对象不计入垃圾回收机制。
基本上,如果要向对象中添加数据又不想干扰垃圾回收机制,就可以使用WeakMap,一个典型应用场景是,在网页的Dom元素上添加数据时就可以使用weakmap结构。
注意,WeakMap弱引用的只是键名而不是键值,键值依然是正常引用的
weakmap没有key()、values()、entries()方法,也没有size属性;只有set()、get()、delete()和has()方法。