Collection
Map
size.has().get().set().delete().clear().keys().values().entries().
const map = new Map([
// You define a map via an array of 2-element arrays. The first
// element of each nested array is the key, and the 2nd is the value
['name', 'Jean-Luc Picard'],
['age', 59],
['rank', 'Captain'],
])
// To get the value associated with a given `key` in a map, you
// need to call `map.get(key)`. Using `map.key` will **not** work.
map.get('name') // 'Jean-Luc Picard'
const map = new Map([])
const n1 = new Number(5)
const n2 = new Number(5)
map.set(n1, 'One')
map.set(n2, 'Two')
// `n1` and `n2` are objects, so `n1 !== n2`. That means the map has
// separate keys for `n1` and `n2`.
map.get(n1) // 'One'
map.get(n2) // 'Two'
map.get(5) // undefined
// If you were to do this with an object, `n2` would overwrite `n1`
const obj = {}
obj[n1] = 'One'
obj[n2] = 'Two'
const two1 = obj[n1] // 'Two'
const two2 = obj[5] // 'Two'
const objectClone = new Map(Object.entries(object))
const arrayClone = new Map(Array.from(map.entries))
const map = new Map([
['name', 'Jean-Luc Picard'],
['age', 59],
['rank', 'Captain'],
])
// The `for/of` loop can loop through iterators
for (const key of map.keys())
console.log(key) // 'name', 'age', 'rank'
for (const value of map.values())
console.log(value) // 'Jean-Luc Picard', 59, 'Captain'
for (const [key, value] of map.entries()) {
console.log(key) // 'name', 'age', 'rank'
console.log(value) // 'Jean-Luc Picard', 59, 'Captain'
}
Set
size.has().add().delete().clear().keys().values().entries().
class XSet extends Set {
union(...sets) {
return XSet.union(this, ...sets)
}
intersection(...sets) {
return XSet.intersection(this, ...sets)
}
difference(set) {
return XSet.difference(this, set)
}
symmetricDifference(set) {
return XSet.symmetricDifference(this, set)
}
cartesianProduct(set) {
return XSet.cartesianProduct(this, set)
}
powerSet() {
return XSet.powerSet(this)
}
// 返回两个或更多集合的并集
// new Set([...setA, ...setB]);
static union(a, ...bSets) {
const unionSet = new XSet(a)
for (const b of bSets) {
for (const bValue of b)
unionSet.add(bValue)
}
return unionSet
}
// 返回两个或更多集合的交集
// new Set([...setA].filter(x => setB.has(x)))
static intersection(a, ...bSets) {
const intersectionSet = new XSet(a)
for (const aValue of intersectionSet) {
for (const b of bSets) {
if (!b.has(aValue))
intersectionSet.delete(aValue)
}
}
return intersectionSet
}
// 返回两个集合的差集
// new Set([...setA].filter(x => !setB.has(x)))
static difference(a, b) {
const differenceSet = new XSet(a)
for (const bValue of b) {
if (a.has(bValue))
differenceSet.delete(bValue)
}
return differenceSet
}
// 返回两个集合的对称差集
static symmetricDifference(a, b) {
// 按照定义, 对称差集可以表达为:
return a.union(b).difference(a.intersection(b))
}
// 返回两个集合 (数组对形式) 的笛卡儿积
// 必须返回数组集合, 因为笛卡儿积可能包含相同值的对
static cartesianProduct(a, b) {
const cartesianProductSet = new XSet()
for (const aValue of a) {
for (const bValue of b)
cartesianProductSet.add([aValue, bValue])
}
return cartesianProductSet
}
// 返回一个集合的幂集
static powerSet(a) {
const powerSet = new XSet().add(new XSet())
for (const aValue of a) {
for (const set of new XSet(powerSet))
powerSet.add(new XSet(set).add(aValue))
}
return powerSet
}
}
WeakMap and WeakSet
WeakMap 结构与 Map 结构基本类似, 唯一的区别就是 WeakMap 只接受非 null 对象作为键名:
- 弱键: 键名构建的引用无法阻止对象执行垃圾回收.
- 不可迭代键: 键/值随时可能被垃圾回收, 无需提供迭代能力, 无
clear()方法.
它的键所对应的对象可能会在将来消失. 一个对应 DOM 元素的 WeakMap 结构, 当某个 DOM 元素被清除, 其所对应的 WeakMap 记录就会自动被移除.
有时候我们会把对象作为一个对象的键用来存放属性值, 普通集合类型比如简单对象 (Object/Map/Set) 会阻止垃圾回收器对这些作为属性键存在的对象的回收, 有造成内存泄漏的危险, WeakMap/WeakSet 则更加内存安全:
- Caching computed results.
- Managing listeners.
- Keeping private data.
弱引用可以缓存计算结果 (无需修改网络层):
const CACHE = new WeakMap()
async function describe(transaction) {
const cached = CACHE.get(transaction)
if (cached) {
return cached
}
// Not cached, do all the work...
CACHE.set(transaction, description)
return description
}
弱引用可以实现控制反转:
const CONVERTERS = new WeakMap()
function getConverter(transaction) {
const converter
= CONVERTERS.get(transaction) || new CurrencyConversion(transaction, { options })
CONVERTERS.set(transaction, converter)
return converter
}
function describe(transaction) {
if (!getConverter(transaction).isReady()) {
// do something..
}
// Here converter only exists for the lifetime to the describe function call.
// When we store it in a weak map, it exists for the entire lifetime of the transaction class.
// That allows us to write code the assumes the same lifetime
// e.g. caching things, using instance equality.
const amount = getConverter(transaction).convert()
}