Set类
集合,就是一组值,与数组类似,但没有索引或顺序,不允许重复。 Set构造函数创建集合对象
let s = new Set()
// 参数必须是一个可迭代对象,可用于去重
let unique = new Set("aoidhfusw38aaaaafn")
Set方法
| 方法/属性 | 说明 | let s = new Set([1,2,3,"4",2]) |
|---|---|---|
| add() | 向集合添加元素,若添加一个数组,添加的是数组,而不是数组的元素 | var num = [1,2]s.add(num // [1,2,3,"4", [1,2]) |
| size | 集合元素个数 | 5 |
| delete() | 删除元素,set中用===判断,删除数组和对象,必须是引用,返回布尔值,删除成功与否 | var tmp = [1,2]s.delete(tmp) // falses.delete(num) // true |
| entries() | 返回一个新的迭代器对象,包含set中按插入顺序排列的所有元素的值的数组,键值相等 | s.entries() // 结果见下图 |
| has() | 判断元素是否在set中 | s.has(1) // true |
| clear() | 清空集合 | s.clear() // |
Notes
Set专门为成员测试做了优化,无论集合有多少成员,has()方法都非常快,数组includes也能判断元素是否在数组中,但是执行速度随着数组大小成反比。
Set内置iteration,可以使用for/of循环枚举所有元素,可以使用...扩展操作符将集合转换为数组或参数
let sum = 0
for (let p of s) {
sum += p
}
[...s]
Math.max(...s)
JavaScript中的Set没有索引,不能通过数组下标的方式取值,但Set并不是绝对无序的,循环枚举时,遍历元素的顺序是其添加进集合的顺序。
除了可迭代,Set类也实现了forEach方法,与数组类似。但由于没有索引,传入forEach的回调函数的第一和第二个参数都是元素的值
let product = 1
s.forEach(n => {product *= n})
常用:数组去重,字符串去重/分割
[...s]
new Set("firefox") // ["f", "i", "r", "e", "o", "x"]
Map类
Map对象表示一组被称为键的值,键值对--映射,映射类似于数组,映射速度也很快,没有数组的索引快,并且允许任何值作为“索引”(key)。任何js值都可以作为键,键的类型判断是===,与set一样
Map()构造函数创建对象
let m = new Map()
let n = new Map([
["one", 1],
["two", 2]
])
构造函数的参数必须为可迭代对象。
let o = {x:1, y:2} // object,不可迭代
let p = new Map(Object.entries(o)) // 相当于new Map([["x", 1], ["y", 2]])
| 方法/属性 | 说明 | let m = new Map([["one", 1],["two", 2]]) |
|---|---|---|
| get() | 通过键获取值 | m.get("one) // 1 |
| size | 元素数量 | 2 |
| set() | 添加/修改键值对 | m.set("three", 3)m.set("one", 111) |
| has() | 是否有键 | m.has("four") // false |
| delete | 通过键,删除键值对 | m.delete("one") |
| clear() | 清空Map |
Notes
链式调用:m.set("one",1).set("two", 2).set("three", 3)
以数组/对象为键,m.set({}, 1).set({}, 2) // m.size =>2,两个对象的引用不同可作为不同的键。且m.get({}) => undefined,因为{}不在键中
m.set(m, undefined) // 把映射自身映射到undefined ,结果见下图
Map内置iteration,迭代的每个元素是一个有两个元素的数组, for/of 常用解构赋值
[...m] // [["one", 1], ["two", 2]]
for (let [key, value] of m) {
console.log(key,value)
}
// one 1
// two 2
与Set类似,Map也是按插入顺序迭代的。获取键/值/键值
[...m.keys()] // ["one", "two]
[...m.vlaues()] // [1, 2]
[...m.entries()] // [["one", 1],["two", 2]] 等价于[...m]
forEach()
m.forEach((value, key) => {}) // 值在前,键在后,==> 值在前,索引在后
WeakMap类 & WeakSet类
WeakMap(弱映射)是Map类的变体,它不会阻止键值被当做垃圾收集。常规的Map对自己的键值保持着强引用,即使对它们的其他引用不存在了,仍然可以通过映射访问这些值。相对而言WeakMap保持着对键值的弱引用,因此无法通过WeakMap访问这些键,进而Weakmap并不影响键值被回收。
WeakMap 与Map的区别
- WeakMap的键必须是对象或数组,原始值(映射本身)不受垃圾收集控制,不能作为键
- WeakMap只实现了get(), set(), delete()和has()。其为不可迭代对象==>如果存在forEach等遍历访问方法,则它的键即为可访问的,也就谈不上是弱引用了。
- 没有size属性,因为弱映射的大小会随着对象被当成垃圾收集而改变。
- 主要用途:实现值与对象的关联而不导致内存泄漏。
WeakSet实现一组对象,没有存储当前对象的列表,不会妨碍这些对象被作为垃圾收集。 WeakMap 弱引用的只是键名,而不是键值。键值依然是正常引用
const wm = new WeakMap();
let key = {};
let obj = {foo: 1};
wm.set(key, obj);
obj = null;
wm.get(key)
// Object {foo: 1}
键值 obj 会在 WeakMap 产生新的引用,修改 obj 不会影响到内部
WeakSet与Set的区别
- 不允许原始值作为成员,只能是对象的集合
- 只实现add(),has(),delete()方法,不可迭代
- 没有size属性
- 用途:对象标记为具有特殊属性或类型,跟踪对象引用
举例例子,说明弱引用:
let obj1 = {a: 1}
let obj2 = {b: 1}
let weakset = new WeakSet([obj1, obj2])
console.log(weakset);
obj1 = null
setTimeout(() => console.log(weakset), 5000)
原始对象垃圾回收后,weakset中的键值对也消失了。