帮你学会ES6中的 数据结构

1,344 阅读8分钟

前言

ES6中新增了几种数据结构,Set, WeakSet, Map, WeakMap, 如果你没有深入了解过,this带你 通过本文一起学习一下吧,查缺补漏,夯实基础

Set

提供的一种新的数据结构集合

Set 本身是一个构造函数,用来生成Set 数据结构,类似于数组,但是里面的值是唯一的,没有重复的值(内部用到了Object.is 方法来进行判断, 除了对 +0 和 -0 的判断和Object.is 不一致)

创建Set

const set = new Set();

Set构造函数的参数也可以接受一个数组或者具有 iterable 接口的可迭代对象(包括其他集合)

PS:如果对iterable还不了解,点击阅读这篇文章

const set = new Set([1,2,3,4,5]) // Set {1,2,3,4,5}
const set1 = new Set(set) // Set {1,2,3,4,5}

向set 中添加成员,且通过Object.is比较,不会添加重复的值,并且添加后不会修改

// 始终返回调用他的集合
set.add()
set.add(0).add('1').add('2') // 可以连续调用
// 可以添加任意值, undefined,null, {}, [], Symbol, NaN, string, number, boolean,function

Object.is(+0,-0) // false
let obj = {}
set.add(obj)
obj.name = 'lalala'
set // 成员不会随着obj属性修改而修改

获取Set实例的成员数

set.size  // 5

删除Set实例中的某个成员

// 删除成功返回true, 删除失败返回false
set.delete(value)

判断Set实例中是否含有某个成员

// 存在返回true, 失败返回false
set.has(0)

清空所有的成员

// 没有返回值
set.clear()

遍历Set实例的方法

Set类是可迭代的,所以可以用for of 来进行枚举集合的所有元素,也可以通过扩展运算符或者Array.from来对set 进行转换为数组。

虽然集合不能向数组一样取得集合中的第几个元素,但是Set 会记住元素插入的顺序,而且始终按这个顺序迭代集合

回调函数方式

forEach()

// 利用回调函数的方式遍历每一个成员
set.forEach((item, index, arr) =>{
	console.log(item, index, arr)
})

// 因为set集合没有索引,所以item 和index 都是set 成员的值

迭代器方式

利用迭代器的方式迭代,返回一个迭代器对象,返回的迭代器对象就可以用for of 来遍历

  • Keys()
// 返回一个迭代器对象
set.keys() // 返回所有的键名,因set没有键名,只有键值,所以返回值

  • Values() // 是默认迭代器 所以可以直接使用...操作或者直接用for..of 遍历

    用[Symbol.iterator]用来获取默认迭代器

    set[Symbol.iterator] === set.values
    set.values() // 键名和键值一样
    
  • Entries()

    set.entries()  // 键名和键值一样
    

WeakSet

如果在set中保存了一个对象成员,但是在有的时候,并不希望一直保存这个对象的引用,这时候,WeakSet就派上了用场,他保持了其弱引用,比如,获取了一个dom的节点并且保存在set中,如果元素在dom树中被删除,但是这个保存的节点对象引用却会仍然存在set中,浪费了内存,无法得到回收,而weakSet却可以在其他地方都没有引用到这个对象的时候,立即释放其内存

WeakSet()构造函数与Set()构造函数类似,也是不重复的值的集合,他实现了一组对象,并且不会妨碍这些对象被垃圾回收程序收集,他只有add(), has(), delete()方法,并且没有size 属性,并且不可迭代

创建WeakSet

let ws = new WeakSet()

初始化WeakSet

构造函数可以接收一个可以迭代的对象(具有Iterable接口的对象)对象中的每个值都会按照迭代顺序插入到WeakSet中去 (注意: 每个值只能是非null的对象类型,否则报错)

const v1 = {name: 'v1'}
const v2 = {name: 'v2'}
const v3 = {name: 'v3'}
const vs1 = new WeakSet([v1,v2,v3]) // 成功
const vs2 = new WeakSet([v1,4]) // TypeError
// 如果初始化的时候只要有一个不是Object类型,则会抛出错误,导致初始化失败

WeakSet 的成员只能是对象或者是继承自Object的类型 (为了保证通过值对象引用才可以取到值。如果允许原始值,则就会没办法区分初始化时使用的字符串字面量和初始化之后使用的是一个相等的字符串了)

添加某个成员

let ws = new WeakSet()
ws.add('x') // 添加一个非对象值会直接抛出TypeError
let x = new String('xxx')
ws.add(x) // WeakSet { String } // 添加成功
ws.add({}) // WeakSet { String, {}} // 添加成功

// add 方法返回集合实例,所以可以操作连起
const v1 = {name: 'v1'}
const v2 = {name: 'v2'}
const v3 = {name: 'v3'}
ws.add(v1).add(v2).add(v3)

删除某个成员

// 删除成功返回true, 删除失败返回false
set.delete(v1)

判断是否存在某个成员

// 存在返回true, 失败返回false
const obj = {name: 'lalala'}
ws.add(obj)
ws.has(obj) // true
ws.has('xxx') // false

当成员中的对象没有指向这个对象的其他引用的时候,这个对象就被当作垃圾回收,这个值就会在集合中消失

不可迭代

因WeakSet中的任意值任何时候都可能被销毁,所以没有迭代的方法,不能遍历,并且也没有clear方法

Map

对象只能用字符串当作键, 所以用数字1和字符串“1”当作属性名,都会被强制转为字符串类型

除此之外,还有一些细微的区别

  1. map的插入速度一般来说比object快

  2. 同内存下。map的存储键值对比object多存储50%

  3. map的删除操作比插入和查找更快,比对象删除好用

  4. 大量操作下,object的查找速度比map快

Map类型是一种存储着键值对的有序列表,是一种新的集合类型。其中的键名和键值可以是任意的数据类型。

创建map

const m = new Map()

初始化的时候可以向map构造函数传入一个数组, 或者传入一个具有Iterator接口,且成员是一个具有双元素的数组

const m = new Map([]) // Map {}
const m = new Map([['name', 'lalala'],['age', 26]]) // Map {name => lalala, age => 26}

// 实际上是把 let arr = [['name', 'lalala'],['age', 26]]
arr.forEach(([key, value]) => m.set(key, value))

添加新的成员

// set 方法,接收两个参数,键名和键值
m.set('name', 'lalala') 
// set 返回实例,可以把多个操作连缀起来

// 如果只传入一个参数,则该参数作为键名,键值为undefined
m.set('age') // Map {age => undefined}

m.set(null, null) // Map {null => null}
m.set(undefined, undefined) // Map {undefined => undefined}

// 如果键名已经存在,则set的时候会把原来的键值覆盖
m.set('a', 1)   // Map {'a' => 1}
m.set('a', 222) // Map {'a' => 222}

// 对象也可以当作键名
let obj = {age: 18}
m.set(obj, obj) // Map {Object => Object}
obj.age = 26
m // Map {{age: 26} => {age: 26}}
// 如果key或者value 是对象,对象里面的属性修改,map里面的值也会修改

获取map中的成员

m.get(key) // 根据key值获取value

// 如果key值不存在,返回undefined 
m.get('xxx') // undefined
m.set(1,22)
m.get(1) // 22

获取map 的成员总数

m.size  // 0

判断指定的键名在Map集合中是否存在

// 存在返回true, 不存在返回false
m.set('a',1) 
m.has('a') // true
m.has('xxxx') // false

删除集合中指定键名的成员

// 删除成功返回true, 删除失败返回false
m.delete('a') // true
m.delete({}) // false

移除所有的成员

m.clear() // 无返回值

遍历Map实例的方法

Map的遍历顺序就是插入顺序,即迭代的第一个键值对是最早添加到映射中,最后一个键值对是最晚添加的。与set 的方法相似。也有迭代器方式和回调函数

回调函数forEach方式

m.forEach((value, key, map) => {
	console.log(value, key, map) // 第一位是value值,第二个是key值,第三个是māp
})
// 传入的回调接受可选的第二个参数,用来指定内部this的指向

迭代器方式

  • keys()

    // 返回所有键名的迭代去对象
    for (let key of m.keys()) {
    	console.log(key)
    }
    
  • values()

// 返回键值的迭代器对象
for (let values of m.values()) {
	console.log(values)
}
  • entries()

​ entries()是map默认的迭代方式

m[Symbol.iterator] === m.entries

所以可以用for..of 或者扩展运算符转为数组

m.set('a', 11)
// 返回成员的迭代器对象
for (let a of m.entries()) {
	console.log(a) // ['a', 11]
}
for (let [key, values] of m.entries()) {
	console.log(key, values) // 'a'  11
}

WeakMap

新增的一种新的集合类型,弱映射,和weakSet相似。属于Map的一个变体,他不会阻止键值被当作垃圾回收,当键值的引用都不存在的时候,就会被释放内存,如果只用对象作为键名,那么weakMap是最好的选择

主要是实现值与对象的关联不导致内存泄漏

键必须为非null类型的对象或者数组, 不能把原始值作为键,键名对应的值可以是任意类型

初始化方法

cont wm = new WeakMap()
// 默认传入一个数组,里面的元素都是包含两个元素的数组,且第一个元素必须是对象,不然就会抛出TypeError
const vm1 = new WeakMap([[{}, 'a'],[{}, 'b']])

添加成员方法set

// 返回成员实例。可以连续set
vm.set({}, 'aa')

get

// get 获取到为value. 如果获取不到返回undefined
let obj = {};
vm.set(obj, 'aa')
vm.get(obj) // 'aa'

has

// 判断是否有这个值 有返回true, 没有返回false
vm.has(obj) // true

delete

// 删除成功返回true, 删除失败返回false
vm.delete(obj)  // true

没有size属性,因为其大小可能随着对象被当作垃圾收集而随时改变

不可迭代

因WeakMap中的任意值任何时候都可能被销毁,所以没有迭代的方法,不能遍历,也没有clear 方法

如有错误,还请指出,共同进步~

看到这里啦,点个赞吧~