Set
介绍
类似数组,新的数据结构,成员的值唯一
可以添加数组、类数组、字符串(或者说是具有 iterable 接口的其他数据结构)作为参数
const set = new Set();
const set = new Set([1,2,3]);
const set = new Set('abcdec');
值的比较算法
使用Same-value-zero equality 算法进行比较,类似 ===
二者区别是对于 NaN 值的比较:前者会认为NaN是相等的,后者 NaN === NaN // false
两个对象,即使是空对象也是不相等的 {}
总结:Set 认为基础数据类型总是相等的,而复杂数据类型总不相等
属性和方法
属性:
size: 返回成员总数
方法:
- 操作方法
方法名称 | 说明 | 返回值 |
---|---|---|
add() | 添加某个值 | 返回 Set 结构本身 |
delete() | 删除某个值 | 返回一个布尔值 |
has() | 是否包含某个值 | 返回一个布尔值 |
clear() | 清除结构所有成员 | 无返回值 |
const set = new Set();
console.log(set.size); // 0
set.add(1);
set.add(2);
set.add(3);
console.log(set.size); // 3
set.delete(2);
console.log(set.size); // 2
console.log(set.has(3)); // true
console.log(set.has(2)); // false
set.clear();
console.log(set.size); // 0
Array.from
可以将 Set 结构转成数组
const set = new Set([1, 2, 3, 4, 3, 5, 6, 6]);
const arr = Array.from(set);
console.log(arr); // [1, 2, 3, 4, 5, 6]
- 遍历方法
遍历顺序是插入顺序,使用 Set 保存一个回调函数列表,调用时可以按照添加顺序调用
方法名称 | 返回值 |
---|---|
keys() | 返回键名的遍历器 |
values() | 返回键值的遍历器 |
entries() | 返回所有成员的遍历器 |
forEach() | 遍历 Map 所有成员 |
const set = new Set(['red', 'green', 'blue']);
for (const item of set.entries()) {
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
可以看到 Set 结构的键值为同一个值,所以,keys() 和 values() 方法返回的是同一个值
for (const item of set.keys()) {
console.log(item);
}
// red
// green
// blue
for (const item of set.values()) {
console.log(item);
}
// red
// green
// blue
forEach()
方法可以操作 Set 成员:
const set = new Set(['red', 'green', 'blue']);
set.forEach((value, key, set) => {
console.log(`key: ${key}; value: ${value};set: ${set};`)
})
// key: red; value: red;set: [object Set];
// key: green; value: green;set: [object Set];
// key: blue; value: blue;set: [object Set];
遍历的作用
- 去重
可以使用扩展运算符去除数组重复成员
const set = new Set(['red', 'green', 'blue']);
// 将 Set 转成数组
let arr = [...set]; // ["red", "green", "blue"]
// 去除数组重复成员
const numArr = [1, 2, 2, 3, 4, 5, 6, 5, 6];
let unique = [...new Set(numArr)]; // [1, 2, 3, 4, 5, 6]
- 给 Set 数据结构使用数组方法
let set = new Set(['red', 'green', 'blue']);
set = new Set([...set].map(x => x + 'ing')); // Set(3) {"reding", "greening", "blueing"}
set = new Set([...set].filter(x => x.length > 3)); // Set(2) {"green", "blue"}
另一个例子:
let set1 = new Set([1, 2, 3]);
let set2 = new Set([2, 3, 6]);
<!-- 合并数组 -->
let union = new Set([...set1, ...set2]); // Set(5) {1, 2, 3, 5, 6}
<!-- 并集 -->
let intersect = new Set([...set1].filter(x => set2.has(x))); // Set(2) {2, 3}
<!-- 交集 -->
let diff = new Set([...set1].filter(x => !set2.has(x))); // Set(1) {1}
WeakSet
- WeakSet 的成员只能是对象,不能是其他类型的值
- WeakSet 中的对象是弱引用(关于引用的相关知识查看文末提示),gc 随时可能把引用清除,所以,ES6 规定 WeakSet 不可遍历;适合临时存放一组对象,以及存放跟对象绑定的信息。只要这些对象在外部消失,它在 WeakSet 里面的引用就会自动消失。
let ws = new WeakSet([1, 2, 3]); // error 不能将类型“number”分配给类型“object”
let ws1 = new WeakSet([[1, 2], [3]]); // ok
方法
方法名称 | 说明 | 返回值 |
---|---|---|
add() | 添加某个值 | 返回 WeakSet 结构本身 |
delete() | 清除实例指定的成员 | 返回一个布尔值 |
has() | 是否包含某个值 | 返回一个布尔值 |
const obj = {};
const foo = {};
let ws = new WeakSet();
ws.add(obj);
ws.add(foo);
ws.delete(foo); // true
ws.has(foo); // false
用途
- 存储 DOM 节点,而不必担心这些节点从文档移除时,会引发内存泄漏
- 由于垃圾回收机制不存储 foos 成员的引用,所以删除实例的时候,不会引发内存泄漏
const foos = new WeakSet();
class Foo {
constructor() {
foos.add(this);
}
methods() {
if (!foos.has(this)) {
throw new TypeError('Foo.prototype.methods 只能在 Foo 的实例上调用!')
}
}
}
Map
一种“值-值”的数据结构,比 Object “字符串-值”的数据结构更为完善的 Hash 结构。
存值方式和 Set 一样,对于基础数据类型,只要严格相等,Map 就认为是一个键;对于复杂数据类型,只要内存地址不同,就是不同的键。
属性和方法
属性:
size: 返回成员总数
方法:
- 操作方法
方法名称 | 说明 | 返回值 |
---|---|---|
set() | 添加某个值 | 返回 Map 结构本身 |
get() | 获取某个值 | 返回 Map 键对应的值 |
delete() | 删除某个值 | 返回一个布尔值 |
has() | 是否包含某个值 | 返回一个布尔值 |
clear() | 清除结构所有成员 | 无返回值 |
- 遍历方法
遍历顺序是插入顺序
方法名称 | 返回值 |
---|---|
keys() | 返回键名的遍历器 |
values() | 返回键值的遍历器 |
entries() | 返回所有成员的遍历器 |
forEach() | 遍历 Map 所有成员 |
与其他数据结构互相转换
- Map 转数组
const myMap = new Map()
.set(true, 7)
.set({foo: 3}, ['abc']);
[...myMap]
// [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]
- 数组转 Map
new Map([
[true, 7],
[{foo: 3}, ['abc']]
])
// Map {
// true => 7,
// Object {foo: 3} => ['abc']
// }
- 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
function objToStrMap(obj) {
let strMap = new Map();
for (let k of Object.keys(obj)) {
strMap.set(k, obj[k]);
}
return strMap;
}
objToStrMap({yes: true, no: false})
// Map {"yes" => true, "no" => false}
- Map 转 JSON
Map 的键名都是字符串:
function strMapToJson(strMap) {
return JSON.stringify(strMapToObj(strMap));
}
let myMap = new Map().set('yes', true).set('no', false);
strMapToJson(myMap)
// '{"yes":true,"no":false}'
Map 的键名都是非字符串
function mapToArrayJson(map) {
return JSON.stringify([...map]);
}
let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']);
mapToArrayJson(myMap)
// '[[true,7],[{"foo":3},["abc"]]]'
- JSON 转 Map
所有键名都是字符串
function jsonToStrMap(jsonStr) {
return objToStrMap(JSON.parse(jsonStr));
}
jsonToStrMap('{"yes": true, "no": false}')
// Map {'yes' => true, 'no' => false}
整个 JSON 是一个数组,每个数组成员又是一个由两个成员的数组
function jsonToMap(jsonStr) {
return new Map(JSON.parse(jsonStr));
}
jsonToMap('[[true,7],[{"foo":3},["abc"]]]')
// Map {true => 7, Object {foo: 3} => ['abc']}
WeakMap
方法名称 | 说明 | 返回值 |
---|---|---|
set() | 添加某个值 | 返回 Map 结构本身 |
get() | 获取某个值 | 返回 Map 键对应的值 |
delete() | 删除某个值 | 返回一个布尔值 |
has() | 是否包含某个值 | 返回一个布尔值 |
WeakMap 的应用场景
- DOM 节点作为键名
let myEle = document.getElementById('logo');
let myWm = new WeakMap();
myEle.set(myEle, { timesClicked: 0 });
myEle?.addEventListener('click', () => {
let logoData = myWm.get(myEle);
logoData.timesClicked++;
}, false);
- 部署私有属性
const _counter = new WeakMap();
const _action = new WeakMap();
class Countdown {
constructor(couter, action) {
_counter.set(this, couter);
_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 ct = new Countdown(2, () => console.log('Down'));
ct.dec();
ct.dec();
// Down

强引用:就像是老板(OOM)的亲儿子一样,在公司可以什么事都不干,但是千万不要老是占用公司的资源为他自己做事,记得用完公司的妹子之后,要让她们去工作(资源要懂得释放) 不然公司很可能会垮掉的。
软引用:有点像老板(OOM)的亲戚,在公司表现不好有可能会被开除,即使你投诉他(调用GC)上班看片,但是只要不被老板看到(被JVM检测到)就不会被开除(被虚拟机回收)。
弱引用:就是一个普通的员工,平常如果表现不佳会被开除(对象没有其他引用的情况下),遇到别人投诉(调用GC)上班看片,那开除是肯定了(被虚拟机回收)。
虚引用:这货估计就是个实习生跟临时工把,遇到事情的时候想到了你,没有事情的时候,秒秒钟拿出去顶锅,开除。
网友评论