Map、weakMap、Set、WeakSet

2020/05/08 javascript

ES6引入了四种新的数据结构:Map(映射)、Set(集合)、WeakSet(弱集合)和weakMap(弱映射)

首先,整理自阮一峰大佬的ES6书籍,原文还请移步:https://es6.ruanyifeng.com/#docs/set-map

1、Set

ES6 提供了新的数据结构 Set。

  1. 它类似于数组,但是成员的值都是唯一的,没有重复的值。
  2. Set加入值的时候,不会发生类型转换,所以5"5"是两个不同的值。
  3. Set加入值时认为NaN等于自身。
  4. 另外,两个对象总是不相等的。
  5. Set的遍历顺序就是插入顺序。
  6. Set结构没有键名,只有键值(或者说键名和键值是同一个值)

Set本身是一个构造函数,用来生成 Set 数据结构。

const set = new Set([1, 2, 3, 4, 4]);
[...set]  // [1,2,3,4]

// 2
const set = new Set([1, 2, 3, 4, '4']);
[...set] // [1, 2, 3, 4, "4"]

const set = new Set();
set.add(NaN)
set.add(NaN)
console.log(set) // Set { NaN }


let set = new Set();

set.add({});
console.log(set.size ) // 1

set.add({});
console.log(set.size ) // 2

1.1 Set的属性和方法

属性:

  1. Set.prototype.constructor:构造函数,默认是Set函数。
  2. Set.size:返回Set实例的成员总数。

方法:

  1. Set.add(value):添加值
  2. Set.delete(value):删除某个值,返回一个布尔值,表示删除是否成功
  3. Set.has(value):判断当前参数是否为Set的成员,返回一个布尔值
  4. Set.clear(value):清除所有值,没有返回值。
  5. Set.keys(value):返回键名的遍历器
  6. Set.values(value):返回键值的遍历器
  7. Set.entries(value):返回键值对的遍历器
  8. Set.forEach(value):使用回调函数遍历每个成员

1.2 Set的使用

使用 Set 可以很容易地实现并集(Union)、交集(Intersect)和差集(Difference)。

let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);

// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}

// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}

// 差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}

2、WeakSet

WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。 首先,WeakSet 的成员只能是对象,而不能是其他类型的值。 其次,WeakSet 中的对象都是弱引用,WeakSet 里面的引用,不计入垃圾回收机制的计数,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。 WeakSet 的成员是不适合引用的,因为它会随时消失。另外,由于 WeakSet 内部有多少个成员,取决于垃圾回收机制有没有运行,运行前后很可能成员个数是不一样的,而垃圾回收机制何时运行是不可预测的,因此 ES6 规定 WeakSet 不可遍历。

正确的例子:

const a = [[1, 2], [3, 4]];
const ws = new WeakSet(a);
// WeakSet {[1, 2], [3, 4]}

错误的例子:

const b = [3, 4];
const ws = new WeakSet(b);
// Uncaught TypeError: Invalid value used in weak set(…)

因为WeakSet的成员必须是对象。

属性和方法: 首先需要确定的是WeakSet没有size属性,也不能遍历。

  1. WeakSet.add(value):添加一个新成员。
  2. WeakSet.delete(value):删除一个成员。
  3. WeakSet.has(value):判断是否存在。

3、Map

Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。

const m = new Map();
const o = {p: 'Hello World'};

m.set(o, 'content')
m.get(o) // "content"

m.has(o) // true
m.delete(o) // true
m.has(o) // false
  1. 如果对同一个键多次赋值,后面的值将覆盖前面的值。
  2. 如果读取一个未知的键,则返回undefined
  3. 只有对同一个对象的引用,Map 结构才将其视为同一个键。
  4. 同样的值的两个实例,在 Map结构中被视为两个键。
  5. 如果 Map 的键是一个简单类型的值(数字、字符串、布尔值),则只要两个值严格相等,Map 将其视为一个键,比如0和-0就是一个键,布尔值true和字符串true则是两个不同的键。另外,undefined和null也是两个不同的键。虽然NaN不严格相等于自身,但 Map 将其视为同一个键。

3.1 属性

size属性返回 Map 结构的成员总数。

const map = new Map();
map.set('foo', true);
map.set('bar', false);

map.size // 2

3.2 方法

Map.set(key, value)set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。

let map = new Map()
  .set(1, 'a')
  .set(2, 'b')
  .set(3, 'c');
console.log(map); // Map { 1 => 'a', 2 => 'b', 3 => 'c' }

Map.get(key):get方法读取key对应的键值,如果找不到key,返回undefined。

const m = new Map();

const hello = function() {console.log('hello');};
m.set(hello, 'Hello ES6!') // 键是函数

console.log(m.get(hello))  // Hello ES6!
console.log(m) // Map { [Function: hello] => 'Hello ES6!' }

Map.has(key):has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。

const m = new Map();

m.set('edition', 6);
m.set(262, 'standard');
m.set(undefined, 'nah');

m.has('edition')     // true
m.has('years')       // false
m.has(262)           // true
m.has(undefined)     // true

Map.delete(key):delete方法删除某个键,返回true。如果删除失败,返回false。

const m = new Map();
m.set(undefined, 'nah');
m.has(undefined)     // true

m.delete(undefined)
m.has(undefined)       // false

Map.clear(key):clear方法清除所有成员,没有返回值。

let map = new Map();
map.set('foo', true);
map.set('bar', false);

map.size // 2
map.clear()
map.size // 0

遍历方法,和Set的一样。Map 的遍历顺序就是插入顺序。

  1. Set.keys(value):返回键名的遍历器
  2. Set.values(value):返回键值的遍历器
  3. Set.entries(value):返回键值对的遍历器
  4. Set.forEach(value):使用回调函数遍历每个成员

3.3 使用

Map转为数组:

const myMap = new Map()
  .set(true, 7)
  .set({foo: 3}, ['abc']);

[...myMap]
// [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]

数组转为Map: 将数组传入 Map 构造函数,就可以转为 Map。

new Map( [[true, 7], [{foo: 3}, ['abc']]])

Map 转为对象: 如果所有 Map 的键都是字符串,它可以无损地转为对象。

function strMapToObj(strMap) {
  let obj = Object.create(null);
  for (let [k,v] of strMap) {
    obj[k] = v;
  }
  return obj;
}

const myMap = new Map()
  .set('yes', true)
  .set('no', false);
strMapToObj(myMap)
// { yes: true, no: false }

对象转为Map:对象转为 Map 可以通过Object.entries()

let obj = {"a":1, "b":2};
let map = new Map(Object.entries(obj));

4、WeakMap

WeakMap结构与Map结构类似,也是用于生成键值对的集合。

WeakMapMap的区别有两点。

  1. WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。
  2. WeakMap的键名所指向的对象,不计入垃圾回收机制。
  3. WeakMapMap 在 API 上的区别主要是两个,一是没有遍历操作(即没有keys()、values()entries()方法),也没有size属性。
  4. WeakMap不支持clear方法。

Search

    Table of Contents