ES6-Set-Map
在 JavaScript 里通常使用 Array 或 Object 来存储数据。但是在频繁操作数据的过程中查找或者统计并需要手动来实现,并不能简单的直接使用。 比如如何保证 Array 是去重的,如何统计 Object 的数据总数等,必须自己去手动实现类似的需求,不是很方便。 在 ES6 中为了解决上述痛点,新增了数据结构 Set 和 Map,它们分别对应传统数据结构的“集合”和“字典”。首先,我们先来学习下 Set 数据结构。它类似于数组,但是成员的值都是唯一的,没有重复的值。
一:Set 基本语法
- 1.生成set实例,可以为空
let s = new Set([1,2])
注意:初始化的参数必须是可遍历的,可以是数组或者自定义遍历的数据结构。
- 2.添加数据
s.add('goodbye')
s.add('hello')
console.log(s) //{1, 2, "hello", "goodbye"}
s.add('hello').add('goodbye')
console.log(s) //{1, 2, "hello", "goodbye"}
注意:Set 数据结构不允许数据重复,所以添加重复的数据是无效的
- 3.删除数据,clear删除全部数据
s.delete('hello')
console.log(s) //{1, 2, "goodbye"}
- 4.统计数据,Set 可以快速进行统计数据
//4.1判断是否包含数据项,返回 true 或 false
console.log(s.has(1)) //1
//4.2计算数据项总数
console.log(s.size) //3
- 5.数组去重
let a1 = [1,2,3,2,3,4,5]
let a = new Set(a1)
console.log(a) //{1, 2, 3, 4, 5}
- 6.合并去重
let b1 = [1,3,4]
let b2 = [3,4,5,7]
let b = new Set([...b1,...b2])
console.log(b) //{1, 3, 4, 5, 7}
console.log([...b]) //[1, 3, 4, 5, 7]
console.log(Array.from(b)) //[1, 3, 4, 5, 7]
- 7.交集
let s1 = new Set(b1)
let s2 = new Set(b2)
let result = new Set(b1.filter(item => s2.has(item)))
console.log(Array.from(result)) //[3, 4]
- 8.差集
let b3 = new Set(b1.filter(item => !s2.has(item)))
let b4 = new Set(b2.filter(item => !s1.has(item)))
console.log(b3) //{1}
console.log(b4) //{5, 7}
console.log([...b3, ...b4]) //[1, 5, 7]
二:遍历方式
1.keys():返回键名的遍历器
2.values():返回键值的遍历器
3.entries():返回键值对的遍历器
4.forEach():使用回调函数遍历每个成员
5.for…of:可以直接遍历每个成员
let m = new Set([1,2,'haihai','tutu'])
console.log(m.keys()) // SetIterator {1, 2, "haihai", "tutu"}
console.log(m.values()) // SetIterator {1, 2, "haihai", "tutu"}
console.log(m.entries()) // SetIterator {1 => 1, 2 => 2, "haihai" => "haihai", "tutu" => "tutu"}
m.forEach(item => {
console.log(item) //1 2 haihai tutu
})
for (let item of m) {
console.log(item) //1 2 haihai tutu
}
for (let item of m.keys()) {
console.log(item) //1 2 haihai tutu
}
for (let item of m.values()) {
console.log(item) //1 2 haihai tutu
}
for (let item of m.entries()) {
console.log(item)
}
//[1,1] [2,2] ["haihai","haihai"] ["tutu","tutu"]
for (let item of m.entries()) {
console.log(item[0], item[1])
}
// 1 1 2 2 haihai haihai tutu tutu
三:WeakSet
WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。
- 1.WeakSet 的成员只能是对象,而不能是其他类型的值。
const qq = new WeakSet()
qq.add(1)
console.log(qq) //Uncaught TypeError: Invalid value used in weak set
qq.add(Symbol())
console.log(qq) //Uncaught TypeError: Invalid value used in weak set
关于WeakSetd 方法,仅有如下
(1).WeakSet.prototype.add(value)
在该 WeakSet 对象中添加一个新元素value.
(2)WeakSet.prototype.clear()
清空该 WeakSet 对象中的所有元素.
(3)WeakSet.prototype.delete(value)
从该 WeakSet 对象中删除value 这个元素
(4)WeakSet.prototype.has(value)
返回一个布尔值, 表示给定的值value是否存在于这个 WeakSet中
let qq = new WeakSet()
const q1 = {
name: 'hello'
}
const q2 = {
age: 18
}
qq.add(q1)
qq.add(q2)
console.log(qq) //{{...},{...}} 0:Object
qq.delete(q1)
console.log(qq) //{{...},{...}} 0:Object
console.log(qq.has(q2)) //true
- 2.WeakSet 对象中存储的对象值都是被弱引用的, 如果没有其他的变量或属性引用这个对象值, 则这个对象值会被当成垃圾回收掉. 正因为这样, WeakSet 对象是无法被枚举的, 没有办法拿到它包含的所有元素。
推荐阅读:Set
四:Map
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。
Map 基本语法
- 1.实例化
let map = new Map()
注意:Iterable 可以是一个数组或者其他 iterable 对象,其元素为键值对(两个元素的数组,例如: [[ 1, ‘one’ ], [ 2, ‘two’ ]])。 每个键值对都会添加到新的 Map。null 会被当做 undefined。
- 2.添加数据
let map = new Map()
let keyObj = {}
let keyFunc = function() {}
let keyString = 'one string'
// 添加键
map.set(keyString, "和键'one string'关联的值")
map.set(keyObj, '和键keyObj关联的值')
map.set(keyFunc, '和键keyFunc关联的值')
console.log(map)
//{"one string" => "和键'one string'关联的值", {…} => "和键keyObj关联的值", ƒ => "和键keyFunc关联的值"}
- 3.删除数据,clear删除所有数据
map.delete(keyObj)
console.log(map)
// {"one string" => "和键'one string'关联的值", ƒ => "和键keyFunc关联的值"}
- 4.统计数据
// 4.1统计所有 key-value 的总数
console.log(map.size) //2
// 4.2判断是否有 key-value
console.log(map.has(keyObj)) // false
- 5.查询数据
console.log(map.get(keyFunc)) // 和键keyFunc关联的值
五:遍历方式
1.keys() :返回一个新的 Iterator 对象。它包含按照顺序插入 Map 对象中每个元素的 key 值
2.values() :返回一个新的 Iterator 对象。它包含按顺序插入Map对象中每个元素的 value 值
3.entries() :返回一个新的包含 [key, value] 对的 Iterator ? 对象,返回的迭代器的迭代顺序与 Map 对象的插入顺序相同
4.forEach() :将会以插入顺序对 Map 对象中的每一个键值对执行一次参数中提供的回调函数
5.for…of :可以直接遍历每个成员
let map = new Map()
let a1 = {}
let a2 = function() {}
let a3 = 'one string'
map.set(a1, "和键a1关联的值")
map.set(a2, '和键a2关联的值')
map.set(a3, '和键a3关联的值')
map.forEach((value, key) => console.log(value, key))
//和键a1关联的值 {} 和键a2关联的值 ƒ () {} 和键a3关联的值 one string
for (let [key, value] of map) {
console.log(key, value)
}
//{} "和键a1关联的值" ƒ () {} "和键a2关联的值" one string 和键a3关联的值
for (let key of map.keys()) {
console.log(key)
}
//{} f () {} one string
for (let value of map.values()) {
console.log(value)
}
//和键a1关联的值 和键a2关联的值 和键a3关联的值
for (let [key, value] of map.entries()) {
console.log(key, value)
}
//{} "和键a1关联的值" ƒ () {} "和键a2关联的值" one string 和键a3关联的值
注意:其实 Object 也是按键值对存储和读取的,那么他俩之间除了我们之前说的区别以外还有其他的吗?
- 1.键的类型
一个Object的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值,包括函数、对象、基本类型。
- 2.键的顺序
Map 中的键值是有序的,而添加到对象中的键则不是。因此,当对它进行遍历时,Map 对象是按插入的顺序返回键值。
- 3.键值对的统计
你可以通过 size 属性直接获取一个 Map 的键值对个数,而 Object 的键值对个数只能手动计算。
- 4.键值对的遍历
Map 可直接进行迭代,而 Object 的迭代需要先获取它的键数组,然后再进行迭代。
- 5.性能
Map 在涉及频繁增删键值对的场景下会有些性能优势。
六:WeakMap
1.WeakMap结构与Map结构类似,也是用于生成键值对的集合。
// WeakMap 可以使用 set 方法添加成员
const wm1 = new WeakMap()
const key = {
foo: 1
}
wm1.set(key, 2)
console.log(wm1) //WeakMap {{Object} => 2}
// WeakMap 也可以接受一个数组,
// 作为构造函数的参数
const k1 = [1, 2, 3]
const k2 = [4, 5, 6]
const wm2 = new WeakMap([
[k1, 'foo'],
[k2, 'bar']
])
console.log(wm2) //WeakMap {Array(3) => "bar", Array(3) => "foo"}
2.WeakMap与Map的区别有两点。
(1).WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。
const map = new WeakMap()
map.set(1, 2)
// TypeError: 1 is not an object!
map.set(Symbol(), 2)
// TypeError: Invalid value used as weak map key
map.set(null, 2)
// TypeError: Invalid value used as weak map key
(2).WeakMap的键名所指向的对象,不计入垃圾回收机制。
推荐阅读:Map