Map
Map 是 JS(es6) 的一种字典数据结构, key 值不重复,如果有重复,就会覆盖前面的,任何值都可以作为 Map 的 key, 包括对象,字符,数字,NaN,symbol。
Map 跟 Object 很像,但是 Object 只能用 string / symbol 作为 Key,Map 可以通过 size 获取键值对个数,而 Object 只能手动计算。
在 JS 中,NaN === NaN
是false,不过在 Map 里 NaN 被认为是同一个 key:
const map = new Map();
map.set(NaN, 123);
map.get(NaN); // 123
但是对于 Object 的 Key,不同的对象,代表的 key 值不同:
new Map().set({}, 1).set({}, 2).size //2
Map 有以下3种创建方式:
const emptyMap = new Map();
const map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three']
]);
const map2 = new Map()
.set(1, 'one')
.set(2, 'two')
.set(3, 'three')
复制 Map:
const original = new Map()
.set(false, 'no')
.set(true, 'yes');
const copy = new Map(original);
通过 key 拿到 value:
const map = new Map();
map.set('foo', 123);
map.get('foo'); // 123
map.get('bar'); // undefined
其他 Map 的方法:
const map = new Map();
// .has() checks if a Map has an entry with a given key
map.has('foo') // true
// .delete() remove entries
map.delete('foo');
map.has('foo');// false
const map1 = new Map()
.set('foo', true)
.set('bar', false);
// .size retrun the number of entries in a Map
map1.size; //2
// .clear() removes all entries of a Map
map1.clear();
map1.size; //0
const map2 = new Map()
.set(false, 'no')
.set(true, 'yes')
// .keys() returns an iterable over the keys of a Map
for(const key of map2.keys()){
console.log(key); // output: false true
}
// we can use spreading(...) to convert iterable returned by .keys() to an Array
[... map2.keys()] // [false, true]
// .values() quite like .keys(), but for values instead of keys
//.entries() return an interable over the entries of a Map
for(const entry of map2.entries()) {
console.log(entry)
//output:
//[false, 'no']
//[ture, 'yes']
}
// we can use sperading(...) to convert iterable returned by .entries() to an Array;
[...map.entries()] // [[false, 'no'], [true, 'yes']]
// we also can use below way to access key and value
for(const [key, value] of map) {
console.log(key, value);
// output:
//false, 'no'
//true, 'yes'
}
只要 Map 只含有 strings 和 symbols 作为 key,那么就可以直接把这个 Map 转为 Object:
const map = new Map([
['a', 1],
['b', 2]
]);
const obj = Object.fromEntries(map); // { a:1, b:2}
也可以把 Object 转换为 Map:
const obj = {
a: 1,
b: 2
}
const map = new Map(Object.entries(obj)); // new Map([['a',1], ['b',2]])
应用: 计算字符串中,字符出现的次数:
function countChars(chars) {
const charCounts = new Map();
for (let ch of chars) {
ch = ch.toLowerCase();
const prevCount = charCounts.get(ch);
prevCount ? charCounts.set(ch, prevCount + 1) : 0;
}
return charCounts;
}
如果想要像数组一样,map 和 filter, 那么就必须要把 Map 先转化为数组:
const originalMap = new Map()
.set(1, 'a')
.set(2, 'b')
.set(3, 'c')
const mappedMap = new Map( //step 3
[...originalMap] //setp 1
.map(([k, v]) => [k * 2, '_' + v]) //step 2
);
// 相当于:new Map([[2, '_a'], [4, '_b'], [6, '_c']])
const filteredMap = new Map( // step 3
[...originalMap] //step 1
.filter(([k, v]) => k < 3) //setp 2
)
// 相当于:new Map([[1,'a'], [2, 'b']])
如果想要,合并两个 Map:
const map1 = new Map()
.set(1, '1a')
.set(2, '1b')
.set(3, '1c');
const map2 = new Map()
.set(2, '2b')
.set(3, '2c')
.set(4, '2d');
const combinedMap = new Map([...map1, ...map2]);
// 相当于:new Map([[1,'1a'],[2,'2b'],[3,'2c'],[4, '2d']])
WeakMap
WeakMap 跟 Map 非常像,只不过多了以下的限制:
- WeakMap 就是一个黑盒子
- 我们不能直接通过 keys / values / entries 来 iterate 或者是 loop WeakMap,并且不能计算它的 size。
- 我们不能清除 WeakMap,如有需要,只能重新创建一个。
- WeakMap 的 key,必须是 objects
const wm = new WeakMap(); wm.set(123, 'test'); // TypeError: Invalid value used as weak map key
- WeakMap 的 key 是弱引用
- 正常来说,如果有对象还被引用,那么就不会被垃圾回收。但是 WeakMap 不一样,在 object 作为 key 的时候,是可以被垃圾回收的,这会导致整个 entry 也被删掉,并且这个没办法检测到这种行为。
const vm = new WeakMap(); { const obj = {}; vm.set(obj, 'attachedValue'); // (A) } // (B)
在 (A) 这一行我们给 obj 这个 key 赋值,但是在 (B) 这一行,obj 这个 entry 就有可能被垃圾回收掉了,但是 vm 还在,并且没办法手动删掉 vm
- 正常来说,如果有对象还被引用,那么就不会被垃圾回收。但是 WeakMap 不一样,在 object 作为 key 的时候,是可以被垃圾回收的,这会导致整个 entry 也被删掉,并且这个没办法检测到这种行为。
应用:
- 用 WeakMap 来保存计算结果
const cache = new WeakMap(); function countOwnKeys(obj) { if (cache.has(obj)) { return [cache.get(obj), 'cached']; } else { const count = Object.keys(obj).length; cache.set(obj, count); return [count, 'computed']; } }
- 用 WeakMap 来保存 private data
const _counter = new WeakMap(); const _action = new WeakMap(); class Countdown { constructor(counter, action) { _counter.set(this, counter); _action.set(this, action); } dec() { let counter = _counter.get(this); counter--; _counter.set(this, counter); if (counter === 0) { _action.get(this)(); } } }
WeakMap 的方法有:
- new WeakMap()
- .delete(key)
- .get(key)
- .has(key)
- .set(key,value)
Set
Set 跟数组很像,但是成员的值都是唯一的,没有重复的值,并且 Set 对象允许存储任何类型的值,无论是原始值或者是对象引用。Set 函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。
有以下三种方式创建 Set:
const emptySet = new Set();
const set = new Set(['red','green','blue']);
const set = new Set()
.add('red')
.add('green')
.add('blue')
常用的 Set 方法:
// .add() addsd an element to a Set
const set = new set();
set.add('red');
// .has() checks if an elements is a member of a Set
set.has('red'); // true
// .delete() removes an element from a Set
set.delete('red'); // true
set.has('red'); // false
// .size contains the number of elements in a Set
const set1 = new Set()
.add('foo')
.add('bar')
set1.size; // 2
// .clear() removes all elements of a Set
set1.clear();
set1.size; //0
// Iterating over Sets
const set2 = new Set(['red', 'green', 'blue']);
for(const x of set2) {
console.log(x)
//outouts:
//'red'
//'green',
//'blue'
}
// use spreading(...) to convert set to array
const set3 = new Set(['red', 'green', 'blue']);
const arr = [...set3]; // ['red', 'green', 'blue']
应用:
移除数组中的重复项:
const set4 = new Set([1,2,1,2,2,3,3,3]);
const arr = [...set4]; // [1,2,3]
字符串是 iterable,所以也可以作为 Set 的参数:
new Set('abc');
new Set(['a', 'b', 'c']); //这两个是一样的
NaN 对于 Set 来说也是一个值,对于任何 Object 都是不同的值:
const set = new Set([NaN, NaN, NaN]);
set.size; //1
cosnt set1 = new Set([{}, {}]);
set.size; //2
Union 两个 Set:
const a = new Set([1,2,3]);
const b = new Set([4,3,2]);
const union = new Set([...a,...b]); // new Set([1,2,3,4])
Intersection 两个 Set:
const a = new Set([1,2,3]);
const b = new Set([4,3,2]);
const intersection = new Set(
[...a]
.filter(x=>b.has(x))
); // new Set([2,3])
Difference 两个 Set:
const a = new Set([1,2,3]);
const b = new Set([4,3,2]);
const difference = new Set(
[...a]
.filter(x=> !b.has(x))
); // new Set([1, 4])
Mapping over Set:
const set = new Set([1,2,3]);
const mappedSet = new Set([...set].map(x=>x*2)); // new Set([2,4,6])
Filtering Set:
const set = new Set([1,2,3,4,5]);
const filteredSet = new Set([...set].filter(x=>(x%2)===0)); // new Set([2,4])
WeakSet
WeakSet 跟 Set 很像,只不过多了下面的限制:
- WeakSet 是个黑盒子
- 我们不能直接通过 keys / values / entries 来 iterate 或者是 loop WeakMap,并且不能计算它的 size。
- 我们不能清除 WeakMap,如有需要,只能重新创建一个。
- WeakSet 的 key 是弱引用
- 正常来说,如果有对象还被引用,那么就不会被垃圾回收。但是 WeakSet 不一样,在 object 作为 key 的时候,是可以被垃圾回收的,这会导致整个 entry 也被删掉,并且这个没办法检测到这种行为。
WeakSet 的方法有:
- new WeakSet()
- .delete(value)
- .get(value)
- .has(value)
- .set(value)