文章目录

  • 一、SET与MAP
  • 二、ES6中的Set
  • 三、回顾数组foreach
  • 四、Set中的foreach
  • 五、将Set转换为数组
  • 六、Weak Set(弱Set)
  • 七、ES6的Map
  • 八、Map的方法
  • 九、Map的初始化
  • 十、Map 上的 forEach 方法
  • 十一、Weak Map



一、SET与MAP

Set 是不包含重复值的列表。你一般不会像对待数组那样来访问 Set 中的某个项;相反更常见的是只在 Set 中检查某个值是否存在Map 则是键与相对应的值的集合。因此, Map 中的每个项都存储了两块数据,通过指定所需读取的键即可检索对应的值。 Map 常被用作缓存,存储数据以便此后快速检索。由于 Set 与 Map 并不正式存在于 ES5 中,开发者就只能使用非数组的对象。
ES6 向 JS 添加了 Set 与 Map

二、ES6中的Set

ES6 新增了 Set 类型,这是一种无重复值的有序列表。 Set 允许对它包含的数据进行快速
访问,从而增加了一个追踪离散值的更有效方式。
创建 Set 并添加项目:

let set = new Set()
set.add(6)
set.add("6")
set.add(6) //重复了,该值会被忽略
console.log(set.size) //2

Set 不会使用强制类型转换来判断值是否重复。这意味着 Set 可以同时包含数值 6 与 字符串 “6” ,将它们都作为相对独立的项(在 Set 内部的比较使用Object.is() 方法,来判断两个值是否相等,唯一的例外是 +0 与 -0 在 Set 中被判断为是相等的)。你还可以向 Set 添加多个对象,它们不会被合并为同一项:

let set = new Set(),
    key1 = {},
    key2 = {}
set.add(key1)
set.add(key2)
console.log(set.size) // 2

你可以使用数组来初始化一个 Set ,并且 Set 构造器会确保不重复地使用这些值。

let set = new Set([1,1,3,4])
console.log(set.size) // 3

Set 构造器实际上可以接收任意可迭代对象作为参数。能使用数组是因为它们默认就是可迭代的, Set 与 Map 也是一样。 Set 构造器会使用迭代器来提取参数中的值。
你可以使用 has() 方法来测试某个值是否存在于 Set 中

let set = new Set([1,1,3,4])
console.log(set.has(3))  // true
console.log(set.has(10)) // false

也可以从 Set 中将值移除。你可以使用 delete() 方法来移除单个值,或调用 clear() 方法来将所有值从 Set 中移除。

let set = new Set([1,1,3,4])
console.log(set.has(3))  // true
set.delete(3)
console.log(set.has(3))  // false
console.log(set.has(4))  // true
set.clear()
console.log(set.has(4))  // fasle

三、回顾数组foreach

以前遍历数组

let arr = [10, 9, 20, -5]
for(let i=0;i<arr.length;i++){
    console.log(arr[i])
}

使用foreach

let arr = [10, 9, 20, -5]
// 将数组中的每个元素按顺序取出来,
// 箭头函数中val是独立的,函数体也是独立的
arr.forEach(val=>console.log(val))

四、Set中的foreach

forEach() 方法会被传递一个回调函数,该回调接受三个参数:

  1. Set 中下个位置的值;
  2. 与第一个参数相同的值;
  3. 目标 Set 自身。
    具有 forEach() 方法的其他对象(即数组与 Map )都会给回调函数传递三个参数,前两个参数都分别是下个位置的值与键(给数组使用的键是数值索引)。
    然而 Set 却没有键。 ES6 标准的制定者本可以将 Set 版本的 forEach() 方法的回调函数设定为只接受两个参数,但这会让它不同于另外两个版本的方法。不过他们找到了一种方式让这些回调函数保持参数相同:将 Set 中的每一项同时认定为键与值。于是为了让 Set 的forEach() 方法与数组及 Map 版本的保持一致,该回调函数的前两个参数就始终相同了。
let set = new Set([1, 2])
set.forEach((value,key,transSet)=>{
    console.log(value + ' ' + key)
    console.log(set === transSet)
})

// 输出结果是:
// 1 1
// true
// 2 2
// true

虽然 Set 能非常好地追踪值,并且 forEach() 可以让你按顺序处理每一项,但是却无法像数组那样用索引来直接访问某个值。如果你想这么做,最好的选择是将 Set 转换为数组。

五、将Set转换为数组

将数组转换为 Set 相当容易,因为可以将数组传递给 Set 构造器;而使用扩展运算符也能简单地将 Set 转换回数组。

let set = new Set([1, 2, 3, 4]),
    array = [...set]
console.log(array) // [1, 2, 3, 4]

六、Weak Set(弱Set)

由于 Set 类型存储对象引用的方式,它也可以被称为 Strong Set 。对象存储在 Set 的一个实例中时,实际上相当于把对象存储在变量中。只要对于 Set 实例的引用仍然存在,所存储的对象就无法被垃圾回收机制回收,从而无法释放内存.

let set = new Set(),
    key = {}
set.add(key)
console.log(set.size)   // 1
// 取消原始引用
key = null
console.log(set.size)   // 1
// 重新获取原始引用
key = [...set][0]

ES6 包含了 Weak Set ,该类型只允许存储对象弱引用,而不能存储基本类型的值。对象的弱引用在它自己成为该对象的唯一引用时,不会阻止垃圾回收.

let set = new WeakSet(),
    key = {}

// 将对象加入set
set.add(key)

console.log(set.has(key))   // true
key = null
console.log(set.has(key))   // false

Weak Set 与正规 Set 的一些共有特征,但是它们还有一些关键的差异:

  1. 对于 WeakSet 的实例,若调用 add() 方法时传入了非对象的参数,就会抛出错误(has() 或 delete() 则会在传入了非对象的参数时返回 false );
  2. Weak Set 不可迭代,因此不能被用在 for-of 循环中;
  3. Weak Set 无法暴露出任何迭代器(例如 keys() 与 values() 方法),因此没有任何编程手段可用于判断 Weak Set 的内容;
  4. Weak Set 没有 forEach() 方法;
  5. Weak Set 没有 size 属性

七、ES6的Map

Set 给了你处理值列表的新方式,不过若需要给这些值添加额外信息,它就没用了。这就是ES6 还添加了 Map 类型的原因。

ES6 的 Map 类型是键值对的有序列表,而键和值都可以是任意类型。键的比较使用的是Object.is() ,因此你能将 5 与 “5” 同时作为键,因为它们类型不同。这与使用对象属性作为键的方式(指的是用对象来模拟 Map )截然不同,因为对象的属性会被强制转换为字符串。

let map = new Map()
map.set('title', 'Hello, ES6')
map.set('year', 2023)
console.log(map.get('title')) // Hello, ES6
console.log(map.get('year'))  // 2023

如果键不存在于map中,则get()方法就返回undefined.

Map可以将对象作为键

let map = new Map(),
    key1 = {},
    key2 = {}
map.set(key1, 6)
map.set(key2, 60)
console.log(map.get(key1))
console.log(map.get(key2))

此代码使用了对象 key1 与 key2 作为 Map 的键,并存储了两个不同的值。由于这些键不会被强制转换成其他形式,每个对象就都被认为是唯一的。这允许你给对象关联额外数据,而无须修改对象自身。

八、Map的方法

以下三个方法在 Map 与 Set 上都存在:
has(key) :判断指定的键是否存在于 Map 中;
delete(key) :移除 Map 中的键以及对应的值;
clear() :移除 Map 中所有的键与值。
Map 同样拥有 size 属性,用于指明包含了多少个键值对。

let map = new Map()
map.set('name', '张三')
map.set('age', 20)

console.log(map.size)  // 2
console.log(map.has('name')) // true
console.log(map.get('name')) // 张三

map.delete('name')
console.log(map.size) // 1
console.log(map.has('name')) // false
console.log(map.get('name')) // undefined

map.clear()

九、Map的初始化

与Set类似,可以将数组传递给Map构造器,以便使用数据来实始化一个Map。该数组中的每一项也必须是数组,内部数组的首个项会作为键,第二项则为对应值。因此整个Map就被这些双项数组所填充。

let map = new Map([['name', '张三'], ['age', 30]])

console.log(map.has('name'))  // 张三
console.log(map.get('age')) // 30
console.log(map.size)  // 2

十、Map 上的 forEach 方法

Map 的 forEach() 方法类似于 Set 与数组的同名方法,它接受一个能接收三个参数的回调函数:

  1. Map 中下个位置的值;
  2. 该值所对应的键;
  3. 目标 Map 自身。
let map = new Map([['name', '张三'], ['age', 30]])

map.forEach((value, key, transMap)=>{
    console.log(key + ' ' + value)
    console.log(map === transMap)
})

// 输出结果是:
// name 张三
// true
// age 30
// true

十一、Weak Map

Weak Map 对 Map 而言,就像 Weak Set 对 Set 一样: Weak 版本都是存储对象弱引用的方式。在 Weak Map 中,所有的键都必须是对象(尝试使用非对象的键会抛出错误),而且这些对象都是弱用,不会干扰垃圾回收。当 Weak Map 中的键在 Weak Map 之外不存在引用时,该键值对会被移除。