ES6中的Map和WeakMap

Map

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

Map实例的属性

1、size
返回Map的成员总数

const m = new Map()
m.set(1,1)
m.set(2,2)
m.size // 2

Map实例的操作方法

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实例的遍历方法

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

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,是实例的弱引用,所以如果删除实例,它们也就随之消失,不会造成内存泄漏。

你可能感兴趣的:(ES6)