【ES6】Set和Map数据结构
Set
Set 本身就是一个构造函数,用来生成 Set 数据结构。类似数组,值是唯一的,没有重复。
const s = new Set();
s.add("a"); // 添加
s.add("b");
console.log(s, s.size); // Set { 'a', 'b' } 2
向 Set 加入值时,不会发生类型转换,即 5 和 "5" 是不一样的值,这类似于严格相等运算符(===)。与 === 不同的是,向 Set 加入 NaN 时,会认为 NaN 等于自身,当重复加入 NaN 会去重;而严格相等运算符则认为 NaN 不等于自身。
两个对象总是不相等的。
Set 函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。
const set = new Set([1, 2, 3, 4]);
console.log(set); // Set { 1, 2, 3, 4 }
const s = new Set([[1, 2], [3, 4]]);
console.log(s); // Set { [ 1, 2 ], [ 3, 4 ] }
数组去重
-
[...new Set(array)] 解构
-
Array.from() 将 Set 结构转为数组
// 数组去重
const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
console.log([...items]); // [1, 2, 3, 4, 5]
// 去除字符串中的重复字符
console.log([...new Set("abbbc")].join("")); // "abc"(将字符串转为 Set 结构,再解构为数组,将其转为字符串)
Set 实例的属性和方法
属性:
-
constructor 构造函数,默认就是 Set 函数
-
size 返回 Set 实例的成员总数
方法:
-
add(value) 添加值,返回 Set 结构自身。可以链式书写。
-
delete(value) 删除值,返回 true / false , 表示是否删除成功。
-
has(value) 查找值,返回 true / false , 表示是否存在该值。
-
clear() 清除所有成员,没有返回值。
遍历
-
keys() 返回键名遍历器对象。
-
values() 返回键值遍历器对象。Set 结构的默认遍历器接口就是 values() 。
-
entries() 返回键值对遍历器对象。
-
forEach() 没有返回值。
Set的遍历顺序就是插入顺序。
Set 结构没有键名,只有键值(或者说键名和键值是同一个值)。
Set 结构的实例默认可遍历,它的默认遍历器生成函数就是它的 values 方法。这意味着,可以省略 values 方法,直接用for...of循环遍历 Set 。
let set = new Set(['red', 'green', 'blue']);
for (let x of set) {
console.log(x);
}
// red
// green
// blue
数组的 map 和 filter 方法也可以间接用于 Set 了。
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}
// (a 相对于 b 的)差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}
WeakSet
与 Set 类似,也是不重复的值的集合。与 Set 不同的是:
-
WeakSet 的成员只能是对象,而不能是其他类型的值。
-
WeakSet 中的对象都是弱引用。即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。
-
WeakSet 不可遍历。成员都是弱引用,随时可能消失,遍历机制无法保证成员的存在,有可能刚遍历完,成员就没了。
用处:可存储 DOM 节点,不用担心这些节点从文档移除时,会引发内存泄漏。
// new WeakSet()
const a = [[1, 2], [3, 4]];
const ws = new WeakSet(a); // WeakSet {[1, 2], [3, 4]}
typeof a; // Object
const b = [1, 2, 3];
const weakSet = new WeakSet(b); // 报错
typeof b; // Object
// new Set()
const c = [2, 3];
const set = new Set(c); // Set { 2, 3 }
数组是类似于列表的高阶对象。 new Set(arr) 和 new WeakSet(arr) 都是将数组成员转为 Set / WeakSet 的成员存入的。第二个例子报错,是因为数组 b 的成员不是对象, WeakSet 的成员只能是对象。
三个方法:
-
add(value) 添加成员。
-
delete(value) 删除成员。返回 true / false 。
-
has(value) 表示是否存在某个值,返回 true / false 。
WeakSet 没有 size 属性,没有办法遍历它的成员。
Map
键值对集合。与对象不同的是,键的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
作为构造函数, Map 也可以接受一个数组作为参数,该数组成员是一个个表示键值对的数组。其实,任何具有 Iterator 接口、且每个成员都是一个双元素的数组(表示 key-value)的数据结构都可以作为 Map 构造函数的参数。
new Map(params) 参数只要满足:1. 具有 Iterator 接口; 2. 其成员是 ["key", "value"] 的形式(双元素的数组)。
const arr = [
['title',"这是标题"],
['content',"这是内容"]
]
const map = new Map(arr);
console.log(map); // Map { 'title' => '这是标题', 'content' => '这是内容' }
console.log(map.has("title")); // true
console.log(map.get("title")); // 这是标题
map.set("author","作者XXX");
console.log(map.size); // 3
只有对同一对象的引用,Map 才将其视为同一个键。相同值的两个实例。在 Map 中被视为两个键。
const map = new Map();
// 相同值的两个实例
const a1 = ['a'];
const a2 =['a'];
map.set(a1, '1').set(a2, '2');
console.log(map); // Map { [ 'a' ] => '1', [ 'a' ] => '2' }
map.clear();
// 同一个实例
const b = ['b'];
map.set(b, 'bbbb');
console.log(map); // Map { [ 'b' ] => 'bbbb' }
map.set(b, 'bccc');
console.log(map); // Map { [ 'b' ] => 'bccc' }
Map 的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。如果 Map 的键是一个简单类型的值(数字、字符串、布尔值),则只要两个值严格相等,Map 将其视为一个键,如 0 和 -0 是同一个键,true 和 "true" 是不同的键。
虽然 NaN 不严格相等于自身,但 Map 将其视为同一个键。
属性和方法
属性:
- size 返回成员总数。
方法:
-
set(key,value) 设置键-值,返回 Map 。可以采用链式写法。
-
get(key) 获取 key 对应的值 value 。找不到 key , 返回 undefined 。
-
has(key) 表示是否存在 key , 返回 true / false 。
-
delete(key) 删除键 key 。返回 true / false 。
-
clear() 清空所有成员。没有返回值。
遍历
-
keys() 返回键名的遍历器。
-
values() 返回键值的遍历器。
-
entries() 返回所有成员的遍历器。Map 的默认遍历器接口就是 entries() 。
-
forEach()
Map 的遍历顺序就是插入顺序。
const arr = [
['title',"这是标题"],
['content',"这是内容"]
]
const map = new Map(arr);
// 扩展运算符,将 Map 结构转为数组结构
console.log([...map]); // [ [ 'title', '这是标题' ], [ 'content', '这是内容' ] ]
// entries()
for(let item of map.entries()) {
console.log(item); // [ 'title', '这是标题' ] , [ 'content', '这是内容' ]
}
// 以上 for...of 代码等同于:
for(let item of map) {
console.log(item); // [ 'title', '这是标题' ] , [ 'content', '这是内容' ]
}
// forEach()
map.forEach((value, key) => {
console.log(`${key}-${value}`); // title-这是标题 , content-这是内容
});
Map 转为对象:如果所有 Map 的键都是字符串,它可以无损地转为对象。如果有非字符串的键名,name这个键名会被转为字符串,再作为对象的键名。
对象转为 Map :
-
方法一( Map 构造函数):通过 Object.entries() 将对象的每一个键值对转为一个数组,返回一个二维数组,再通过 new Map(arr) 转为 Map 结构。
-
方法二( Map 的 set 方法):通过 Object.keys() 获取对象的属性名,作为 Map 结构的 key , 将对象的属性值作为 Map 结构的 value 。
const obj = {
"title":"标题",
"content":"内容"
}
console.log(Object.entries(obj)); // [ [ 'title', '标题' ], [ 'content', '内容' ] ]
// 对象转为 Map
// 方法一:Map 构造函数
const map = new Map(Object.entries(obj));
console.log(map); // Map { 'title' => '标题', 'content' => '内容' }
// 方法二:Map 的 set 方法 添加成员
const m = new Map();
for(const k of Object.keys(obj)) {
m.set(k, obj[k]);
}
console.log(m); // Map { 'title' => '标题', 'content' => '内容' }
Map 转为 JSON :
-
key 都是字符串的情况:将 Map 先转为对象,再通过 JSON.stringify(obj) 转为 对象 JSON 。
-
key 有非字符串的情况:将 Map 先转为数组,再通过 JSON.stringify(arr) 转为 数组 JSON 。
JSON 转为 Map :
-
key 都是字符串的情况:通过 JSON.parse(jsonStr) 转为对象,再将对象转为 Map 。
-
key 有非字符串的情况:通过 JSON.parse(jsonStr) 转为数组,再将数组转为 Map 。
WeakMap
与 Map 类似,也是用于生成键值对的集合。只接受对象作为键名。
特点同 WeakSet 。
const wm = new WeakMap();
let key = {};
let obj = {foo: 1};
wm.set(key, obj);
obj = null;
wm.get(key);// Object {foo: 1}
注意,WeakMap 弱引用的只是键名,而不是键值。键值依然是正常引用。所以,即使在 WeakMap 外部消除了 obj 的引用,WeakMap 内部的引用依然存在。
WeakRef
用于直接创建对象的弱引用。
let target = {};
let wr = new WeakRef(target); // 创建了一个机遇 target 的新对象 wr
上面代码中, wr 就是一个 WeakRef 的实例,属于对 target 的弱引用。
WeakRef 实例对象有一个 deref() 方法,如果原始对象存在,该方法返回原始对象。如果原始对象已经被垃圾回收机制清除,该方法返回 undefined 。
弱引用对象的一大用处,就是作为缓存,未被清除时可以从缓存取值,一旦清除缓存就自动失效。
一旦使用 WeakRef() 创建了原始对象的弱引用,那么在本轮时间循环,原始对象肯定不会被清除,只会在后面的事件循环才会被清除。

浙公网安备 33010602011771号