ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。
const m = new Map()
const k = {key:'value'}
m.set(k,'content') //k作为一个键
m.get(k)
m.has(k)
给Map传入一个数组作为参数:
const m = new Map([['name','小明'],['age',20]])
console.log(m.has('name')) //true
console.log(m.has('age')) // true
console.log(m.get('name')) // 小明
console.log(m.get('age')) // 20
虽然参数一个数组,其内部的实现是
const arr = [['name','小明'],['age',20]]
const m = new Map()
arr.forEach(
([key,value])=>map.set(key,value)
)
对同一个键多次赋值,后面的值会覆盖前面的值
const m = new Map()
m.set('a','a')
m.set('a','b')
console.log(m.get('a')) // b
只有对同一个对象的引用,Map才将其视为一个键
const map = new Map();
map.set(['a'], 'a');
map.get(['a']) // undefined
set和get中的键虽然值相同,但是缺失不同的数组实例,内存地址不一样,所以get无法读取set中的键,返回undefined。
Map 的键是跟内存地址绑定的,只要内存地址不一样,就视为两个键。
如果键是一个基础数据类型的值(number等),只要是严格相等(===) ,就是同一个键。(NaN是特例,是同一个键)
const m = new Map()
m.set(true,'true')
m.set('true','trueString')
map.get(true) // true
1、size
返回Map的成员总数
const m = new Map()
m.set(1,1)
m.set(2,2)
m.size // 2
1、set
设置键值对
c
set
方法返回Map对象,因此可以采用链式写法。
2、get
读取key
对应的值,如果没有就返回undefined
const m = new Map()
m.set(1,1)
m.get(1) // 1
m.get(2) // undefined
3、has
判断Map对象是否存在某个键,返回Boolean值
const m = new Map()
m.set(1,1)
m.has(1) // true
m.has(2) // false
4、delete
删除Map中的某个键,成功返回true,失败返回false
const m = new Map()
m.set(1,1)
m.delete(1) //true
5、clear
清除所有成员,没有返回值
Map跟Set一样,都具有遍历器生成函数和遍历方法。
keys() 返回键名
values() 返回键值
entries() 返回键值对(默认遍历器接口,采用for of遍历)
forEach() 使用回调函数遍历成员
const map = new Map([
[1, '1'],
[2, '2'],
]);
for (let key of map.keys()) {
console.log(key);
}
// 1
// 2
for (let value of map.values()) {
console.log(value);
}
// "1"
// "22"
for (let item of map.entries()) {
console.log(item[0], item[1]);
}
// 1 "1"
// 2 "2"
// 或者
for (let [key, value] of map.entries()) {
console.log(key, value);
}
// 1 "1"
// 2 "2"
// 等同于使用map.entries()
for (let [key, value] of map) {
console.log(key, value);
}
// 1 "1"
// 2 "2"
map.forEach(function(value, key, map) {
console.log(key, value);
});
采用扩展运算符,将Map对象转为数组
const m = new Map([
[1, '1'],
[2, '2'],
]);
[...m.keys()] // [1,2]
[...m.values()] // ['1','2']
[...m.entries()] // [[1,'1'],[2,'2']]
[...m] // [[1,'1'],[2,'2']]
WeakMap结构与Map类似,也是用于生成键值对的集合。
WeakMap
只接受对象作为键名(null
除外),不接受其他类型的值作为键名。
同样,WeakMap
的键名指向的对象,是不计入垃圾回收机制的,跟WeakSet
相同。它的键名所引用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内。因此,只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。也就是说,一旦不再需要,WeakMap 里面的键名对象和所对应的键值对会自动消失,不用手动删除引用。WeakMap
结构有助于防止内存泄漏。
注意,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
的引用,WeakMap 内部的引用依然存在。
WeakMap 的一个用处是部署私有属性。
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);
if (counter < 1) return;
counter--;
_counter.set(this, counter);
if (counter === 0) {
_action.get(this)();
}
}
}
const c = new Countdown(2, () => console.log('DONE'));
c.dec()
c.dec()
// DONE
上面代码中,Countdown
类的两个内部属性_counter
和_action
,是实例的弱引用,所以如果删除实例,它们也就随之消失,不会造成内存泄漏。