Set 数据结构
定义:Set 是一种类似数组的数据结构,但其成员的值都是唯一的(自动去重)。
创建与传参:作为构造函数,它可以接收任何具有 iterable 接口的数据结构(如数组、字符串)作为参数来初始化。
javascript
new Set([1, 2, 3]); // 来自数组
new Set('abc'); // 来自字符串
实例属性和方法
| 属性/方法 | 说明与示例 | 返回值 |
|---|---|---|
set.size | 返回Set实例的成员总数。 | Number |
set.add(value) | 添加某个值,返回Set本身(支持链式调用)。 | Set 对象 |
set.has(value) | 判断该值是否为Set的成员。 | Boolean |
set.delete(value) | 删除某个值。 | Boolean(表示是否删除成功) |
set.clear() | 清除所有成员。 | undefined |
Set 与数组的相互转换
-
Set => 数组(最常用的去重方法):
javascript
// 方法1:扩展运算符 let arr1 = [...new Set([1, 2, 2, 4])]; // [1, 2, 4] // 方法2:Array.from let arr2 = Array.from(new Set([1, 2, 4])); -
数组 => Set:
javascript
new Set([1, 2, 3]); // 自动去重
遍历方法
Set 结构的键名和键值是同一个值,因此 keys() 和 values() 的行为完全一致。
Set.prototype.keys():返回键名的遍历器。Set.prototype.values():返回键值的遍历器。Set.prototype.entries():返回键值对的遍历器(每项为[value, value])。Set.prototype.forEach():使用回调函数遍历每个成员。回调函数参数为(value, value, set)。
WeakSet
定义与特点:
- 成员只能是对象h或者Symblo值,不能是其他类型的值。
- 对成员对象是弱引用。这意味着,如果没有其他变量引用该对象,即使它存在于
WeakSet中,垃圾回收机制也会自动回收其占用的内存。 - 由于成员可能随时被回收,
WeakSet没有size属性,也无法被遍历。
创建与传参:
构造函数接受的参数必须是一个可迭代对象,且其每个成员都是对象。通常使用二维数组,因为内层数组的成员才是 WeakSet 的值。
javascript
// 正确:二维数组,成员[1,2]和[3,4]是对象(数组是引用类型)
new WeakSet([[1, 2], [3, 4]]);
// 错误:一维数组成员不是对象
// new WeakSet([1, 2, 3]);
弱引用示例:
javascript
let obj = {name: 'qin'};
const ws = new WeakSet([obj]);
obj = null; // 此后,{name: 'qin'} 对象可能被垃圾回收,无法从 ws 中取回。
let obj = {name: 'qin'};
const ws = new Set([obj]);
obj = null; // obj存储解析:obj在栈中定义key:value的形式,key是obj,value是hash值,obj在堆内开辟一个空间地址,用来存储{name:'qin'},hash值指向了堆内存中对应的地址,手动重置obj时,只是重置了obj对空间地址的引用,ws还会保持对空间地址的引用,所以堆内存的空间没有被gc释放。
实例方法:
WeakSet.prototype.add(object):添加对象成员。WeakSet.prototype.has(object):判断对象是否存在。WeakSet.prototype.delete(object):删除对象成员。
Map 数据结构
定义:Map 是一种完善的“键值对”集合数据结构。与传统对象(Object)只能用字符串或 Symbol 作为键不同,Map 的“键”可以是任何类型的值(包括对象、函数)。
创建与传参:
构造函数可以接受一个可迭代对象作为参数,该对象的每个成员必须是一个表示键值对的二元数组。
javascript
// 正确:传入一个二维数组,每个子数组是[key, value]
new Map([['name', 'qin'], [{id: 1}, 'objectKey']]);
实例属性和方法
| 属性/方法 | 说明与示例 | 返回值 |
|---|---|---|
map.size | 返回 Map 实例的成员总数。 | Number |
map.set(key, value) | 设置键值对。返回Map本身,支持链式调用。 | Map 对象 |
map.get(key) | 读取指定键对应的值。 | value 或 undefined |
map.has(key) | 判断是否有指定的键。 | Boolean |
map.delete(key) | 删除某个键。 | Boolean |
map.clear() | 清除所有成员。 | undefined |
遍历方法
Map.prototype.keys():返回键名的遍历器。Map.prototype.values():返回键值的遍历器。Map.prototype.entries():返回所有成员(键值对) 的遍历器。Map.prototype.forEach():遍历所有成员。回调函数参数为(value, key, map)。
与其他数据结构的转换
-
Map => 数组(使用扩展运算符最方便):
javascript
const myMap = new Map().set('a', 1).set('b', 2); console.log([...myMap]); // [ ['a', 1], ['b', 2] ] -
对象 => Map(借助
Object.entries):javascript
let obj = {"a": 1, "b": 2}; let map = new Map(Object.entries(obj)); -
Map => 对象(当Map的键都是字符串时):
javascript
function mapToObj(map) { let obj = Object.create(null); for (let [k, v] of map) { obj[k] = v; } return obj; }
WeakMap
定义与特点:
- 只接受对象(
null除外) 作为键名,不接受其他类型的值。 - 对键名所指向的对象是弱引用,不影响垃圾回收。键名对象被回收后,对应的键值对会自动从
WeakMap中移除。 - 因此,
WeakMap没有size属性,也无法被遍历。 - 应用场景:常用于需要将额外数据与对象关联,又不想影响对象本身生命周期的情况(如DOM元素作为键名存储元数据)。
对象的迭代
普通对象 {} 默认是不可迭代的,不能直接用于 for...of 循环。
遍历普通对象的几种方式:
-
for...in循环(遍历自身和继承的可枚举属性键名,通常需搭配hasOwnProperty过滤):javascript
for (const key in obj) { if (obj.hasOwnProperty(key)) { console.log(key, obj[key]); } } -
Object.keys()+for...of(遍历自身可枚举属性键名):javascript
for (const key of Object.keys(obj)) { console.log(key, obj[key]); } -
Object.values()+for...of(遍历自身可枚举属性值):javascript
for (const value of Object.values(obj)) { console.log(value); } -
Object.entries()+for...of(遍历自身可枚举属性键值对):javascript
for (const [key, value] of Object.entries(obj)) { console.log(key, value); }
让普通对象可迭代
通过自定义 [Symbol.iterator] 方法,可以使普通对象支持 for...of 循环。
javascript
const obj = {
a: 1,
b: 2,
[Symbol.iterator]: function* () {
yield* Object.entries(this); // 使用生成器函数委托给 entries
}
};
for (const [key, value] of obj) { // 现在可以 for...of 了
console.log(key, value);
}
总结
| 特性 | Set | Map | WeakSet | WeakMap |
|---|---|---|---|---|
| 核心特点 | 值唯一的集合 | 键值对集合,键类型任意 | 弱引用对象集合 | 弱引用对象作为键的集合 |
| 键/值类型 | 值可以是任何类型 | 键可以是任何类型 | 值必须是对象 | 键必须是对象 |
| 可遍历性 | 是 | 是 | 否 | 否 |
size 属性 | 有 | 有 | 无 | 无 |
| 垃圾回收影响 | 强引用,会阻止成员被回收 | 强引用,会阻止键被回收 | 弱引用,不影响成员被回收 | 弱引用,不影响键对象被回收 |