ES6引入了四种新的数据结构:Map(映射)、Set(集合)、WeakSet(弱集合)和weakMap(弱映射)
首先,整理自阮一峰大佬的ES6书籍,原文还请移步:https://es6.ruanyifeng.com/#docs/set-map
1、Set
ES6 提供了新的数据结构 Set。
- 它类似于数组,但是成员的值都是唯一的,没有重复的值。
- 向
Set
加入值的时候,不会发生类型转换,所以5
和"5"
是两个不同的值。 - 向
Set
加入值时认为NaN等于自身。 - 另外,两个对象总是不相等的。
Set
的遍历顺序就是插入顺序。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的属性和方法
属性:
Set.prototype.constructor
:构造函数,默认是Set
函数。Set.size
:返回Set
实例的成员总数。
方法:
Set.add(value)
:添加值Set.delete(value)
:删除某个值,返回一个布尔值,表示删除是否成功Set.has(value)
:判断当前参数是否为Set
的成员,返回一个布尔值Set.clear(value)
:清除所有值,没有返回值。Set.keys(value)
:返回键名的遍历器Set.values(value)
:返回键值的遍历器Set.entries(value)
:返回键值对的遍历器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
属性,也不能遍历。
WeakSet.add(value)
:添加一个新成员。WeakSet.delete(value)
:删除一个成员。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
- 如果对同一个键多次赋值,后面的值将覆盖前面的值。
- 如果读取一个未知的键,则返回
undefined
。 - 只有对同一个对象的引用,
Map
结构才将其视为同一个键。 - 同样的值的两个实例,在
Map
结构中被视为两个键。 - 如果 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 的遍历顺序就是插入顺序。
Set.keys(value)
:返回键名的遍历器Set.values(value)
:返回键值的遍历器Set.entries(value)
:返回键值对的遍历器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
结构类似,也是用于生成键值对的集合。
WeakMap
与Map
的区别有两点。
WeakMap
只接受对象作为键名(null除外)
,不接受其他类型的值作为键名。WeakMap
的键名所指向的对象,不计入垃圾回收机制。WeakMap
与Map
在 API 上的区别主要是两个,一是没有遍历操作(即没有keys()、values()
和entries()
方法),也没有size
属性。WeakMap
不支持clear
方法。