跳到主要内容
JavaScript 集合 (Set、WeakSet) 与映射 (Map、WeakMap) | 极客日志
JavaScript 大前端 算法
JavaScript 集合 (Set、WeakSet) 与映射 (Map、WeakMap) JavaScript 集合 (Set、WeakSet) 与映射 (Map、WeakMap) 是 ES6 引入的核心数据结构。Set 存储唯一值,支持去重和集合运算;WeakSet 仅存对象且弱引用,便于生命周期管理。Map 提供任意类型键的键值对映射,保持插入顺序;WeakMap 以对象为键且不可遍历,适合私有数据存储。掌握这些结构能优化性能并避免内存泄漏。
星云 发布于 2026/3/24 更新于 2026/5/22 13 浏览一、Set 数据类型
JavaScript 中的 Set 是 ES6(ES2015)引入的一种集合数据结构 ,用于存储唯一值 (unique values)的有序列表。无论是原始类型(如数字、字符串)还是对象引用,Set 都会自动去重。
基本特性
特性 说明 值唯一 不允许重复元素(使用 === 判断相等,但 NaN === NaN 被视为相等)。 有序 元素按插入顺序迭代。 可存储任意类型 包括 number、string、object、NaN、undefined 等。 非索引结构 不能通过下标访问(不像数组),但可遍历。
注意:Set 中的 {} 和 {} 被视为不同对象 (因为引用不同),所以不会去重。
详细介绍
1. 常用方法与属性
方法 说明 示例 add(value)添加元素(返回 Set 自身,可链式调用)。 s.add(4)delete(value)删除元素(返回布尔值)。 s.delete(1) → truehas(value)检查是否存在(返回布尔值)。 s.has(2) → trueclear()清空所有元素。 s.clear()forEach(callback)遍历元素。 s.forEach(v => console.log(v))
属性:size——返回元素个数。
s2.size ;
创建 Set。
const s1 = new Set ();
s2 = ([ , , , , ]);
s3 = ( );
const
new
Set
1
2
3
2
1
const
new
Set
'hello'
2. Set 与 Array 互转 场景 推荐 需要去重 Set需要频繁判断元素是否存在 Set(has() 时间复杂度 O(1),Array 的 includes() 是 O(n))需要索引/顺序操作(如 sort, splice) Array存储大量数据且频繁增删 Set 更高效
const s = new Set ();
s.add (NaN );
s.add (NaN );
console .log (s.size );
s.add (0 );
s.add (-0 );
console .log (s.size );
const arr = [1 , 2 , 2 , 3 ];
const set = new Set (arr);
const unique = [...new Set ([1 , 2 , 2 , 3 ])];
const set = new Set ([1 , 2 , 3 ]);
const arr = [...set];
Set 是可迭代对象(iterable),支持以下方式遍历:
const set = new Set ([1 , 2 , 3 ]);
for (const item of set) {
console .log (item);
}
set.forEach ((value, valueAgain, setRef ) => {
console .log (value);
});
[...set];
Array .from (set);
3. 高级用法示例 const users = [
{ id : 1 , name : 'Alice' },
{ id : 2 , name : 'Bob' },
{ id : 1 , name : 'Alice' }
];
const uniqueUsers = Array .from (
new Set (users.map (u => u.id )),
id => users.find (u => u.id === id)
);
const uniqueUsersMap = Array .from (new Map (users.map (u => [u.id , u])).values ());
const a = [1 , 2 , 3 ];
const b = [3 , 4 , 5 ];
const union = [...new Set ([...a, ...b])];
const intersection = [...new Set (a.filter (x => b.includes (x)))];
const diff = a.filter (x => !new Set (b).has (x));
4. 注意事项
5. 总结
Set 是处理'唯一值集合'的最佳工具 ,尤其适合去重、成员检测、集合运算等场景。结合扩展运算符和数组方法,能写出简洁高效的代码。
常用口诀:
去重要用 Set 。
存在检查用 .has() 。
遍历用 for...of 或 forEach 。
转数组用 [...set] 。
二、WeakSet 数据类型 WeakSet 是 JavaScript(ES6 引入)中一种特殊的集合数据结构 ,它与 Set 类似,但有关键限制和用途 ,那就是 WeakSet 只能存储对象(不能是原始值),且对对象的引用是'弱引用'(weakly held)——不会阻止垃圾回收(GC)。
核心特性 特性 说明 只能存对象 不能添加 number、string、boolean 等原始值。 弱引用 存入的对象如果没有其他引用,会被 GC 自动回收。 不可迭代 没有 .size、.clear()、.entries()、for...of 等方法。 无顺序 不保证元素顺序(也无法遍历)。 私有性 无法知道 WeakSet 中有哪些对象(设计如此)。
基本用法 方法 说明 示例 add(value)添加对象。 ws.add(obj)has(value)检查对象是否存在。 ws.has(obj) → true/falsedelete(value)删除对象。 ws.delete(obj)
const obj = {};
ws.add (obj);
console .log (ws.has (obj));
ws.delete (obj);
console .log (ws.has (obj));
没有 size 属性! 你无法知道 WeakSet 里有多少元素。
const ws = new WeakSet ();
ws.add ({});
ws.add (document .body );
ws.add (new Date ());
ws.add (42 );
ws.add ('hello' );
ws.add (true );
典型场景
const requestMetadata = new WeakSet ();
fetch ('/api/data' ).then (res => {
requestMetadata.add (res);
});
const privateData = new WeakSet ();
class User {
constructor (name ) {
this .name = name;
privateData.add (this );
}
isValid ( ) {
return privateData.has (this );
}
}
const processedElements = new WeakSet ();
function processElement (el ) {
if (processedElements.has (el)) {
return ;
}
processedElements.add (el);
}
WeakSet vs Set 特性 SetWeakSet存储类型 任意值(对象/原始值) 仅对象 引用类型 强引用(阻止 GC) 弱引用(不阻止 GC) 可遍历 是(for...of, .size 等) 否(完全不可遍历) 用途 通用去重、集合运算 生命周期绑定对象的标记/元数据
用 Set 存'我要主动管理的数据'。
用 WeakSet 存'这个对象有某种状态,但我不负责它的生死'。
重要注意事项
弱引用 ≠ 立即回收 。
对象是否被回收取决于 GC 时机。
即使 WeakSet 是唯一引用,也可能暂时未被回收。
不能用于原始值 。
const ids = new WeakSet ();
ids.add (123 );
无法遍历 = 无法知道内容 。
const ws = new WeakSet ();
ws.add ({ a : 1 });
ws.add ({ b : 2 });
这是刻意设计 :防止开发者依赖内部状态,确保弱引用语义。
高级技巧:结合 FinalizationRegistry(ES2021) const registry = new FinalizationRegistry ((heldValue ) => {
console .log (`${heldValue} 被回收了` );
});
const ws = new WeakSet ();
const obj = { id : 1 };
ws.add (obj);
registry.register (obj, 'Object with id=1' );
obj = null ;
FinalizationRegistry 是实验性 API,不推荐常规使用。
总结 WeakSet 是一个'只增不查全貌'的对象标记工具 ,核心价值在于:'我知道这个对象有某种状态,但我不持有它,让它自由生灭。'
存对象 (不能存数字/字符串)。
做标记 (如'已初始化'、'已验证')。
免清理 (随对象自动消失)。
别遍历 (根本做不到)。
别计数 (没有 .size)。
三、Map 数据类型 Map 是 JavaScript(ES6/ES2015 引入)中一种键值对集合(key-value collection) 数据结构,用于存储任意类型的键和值的映射关系。它比普通对象({})更强大、更灵活,尤其适合键不是字符串 或需要精确控制键值行为 的场景。
Map vs 普通对象 特性 普通对象 {} Map键的类型 仅字符串/Symbol 任意类型 (包括对象、函数、数字等)键的顺序 无序(ES2015 后部分有序) 插入顺序 (严格保持)大小获取 需手动计算(Object.keys(obj).length) 直接 .size 迭代 需 Object.keys() 等辅助 原生可迭代(for...of、.entries() 等) 原型污染风险 有(如 obj.__proto__) 无 (纯净数据结构)性能 大量动态属性时较慢 大量数据时更快 (专为频繁增删优化)
简单说:Map 是'真正的哈希表',而对象是'为固定结构设计的'。
基本用法
1. 遍历 Map const keys = [...map.keys ()];
const values = [...map.values ()];
const entries = [...map.entries ()];
for (const key of map.keys ()) { ... }
for (const value of map.values ()) { ... }
for (const entry of map.entries ()) {
console .log (entry);
}
for (const [key, value] of map) {
console .log (key, value);
}
2. 核心方法 方法 说明 示例 set(key, value)添加/更新键值对。 map.set('a', 1)get(key)获取值(不存在返回 undefined)。 map.get('a') → 1has(key)检查是否存在键。 map.has('a') → truedelete(key)删除键值对(返回布尔值)。 map.delete('a')clear()清空所有键值对。 map.clear()size属性:返回元素数量。 map.size → 0
const map = new Map ();
map.set ('name' , 'Bob' );
map.set (1 , 'number key' );
map.set ({}, 'object key' );
console .log (map.get ('name' ));
console .log (map.has (1 ));
console .log (map.size );
const map = new Map ();
const map2 = new Map ([
['name' , 'Alice' ],
[42 , 'answer' ],
[{ id : 1 }, 'user object' ]
]);
关键特性详解
用 DOM 元素作键 → 存储其状态。
用函数作键 → 缓存计算结果。
const map = new Map ();
map.set (3 , 'three' );
map.set (1 , 'one' );
map.set (2 , 'two' );
console .log ([...map.keys ()]);
键的比较使用 SameValueZero 算法(类似 ===,但 NaN === NaN 为真)。
const map = new Map ();
map.set (NaN , 'not a number' );
console .log (map.get (NaN ));
const user1 = { id : 1 };
const user2 = { id : 2 };
const userMap = new Map ();
userMap.set (user1, 'Alice' );
userMap.set (user2, 'Bob' );
console .log (userMap.get (user1));
console .log (userMap.get ({ id : 1 }));
Map vs WeakMap 特性 MapWeakMap键的类型 任意 仅对象 可枚举 是 否(无 size、clear()、entries() 等) 垃圾回收 键被强引用(阻止 GC) 弱引用 (键可被 GC 回收)用途 通用键值存储 私有数据、DOM 元素元数据
WeakMap 适合:不希望阻止对象被回收的场景 (如给 DOM 元素附加临时数据)。
实用技巧 const cache = new Map ();
function expensiveFn (arg ) {
if (cache.has (arg)) return cache.get (arg);
const result = ;
cache.set (arg, result);
return result;
}
const users = [{ id : 1 }, { id : 2 }, { id : 1 }];
const unique = [...new Map (users.map (u => [u.id , u])).values ()];
const map = new Map ([['a' , 1 ], ['b' , 2 ]]);
const obj = Object .fromEntries (map);
const obj = { a : 1 , b : 2 };
const map = new Map (Object .entries (obj));
注意事项 map.set ({ x : 1 }, 'value' );
map.get ({ x : 1 });
不能用 JSON.stringify() 直接序列化
JSON .stringify (new Map ());
JSON .stringify ([...map]);
性能建议 场景 推荐 键是字符串/数字,结构固定 普通对象 {} 键类型多样、动态增删频繁 Map需要与对象互转(仅字符串键) Object.entries()/Object.fromEntries()存储私有数据且不阻止 GC WeakMap
总结 Map 是现代 JavaScript 中处理键值对的首选数据结构 ,当你遇到以下情况时,优先考虑它:
键不是字符串(如对象、数字、Symbol)。
需要频繁增删键值对。
需要可靠获取元素数量。
需要保持插入顺序。
担心原型污染。
const m = new Map ();
m.set (key, value);
m.get (key);
m.has (key);
m.delete (key);
m.size ;
四、WeakMap 数据类型 WeakMap 是 JavaScript(ES6 引入)中一种键值对集合(key-value collection) ,它是 Map 的'弱引用'版本,专为以对象为键、且不希望阻止垃圾回收(GC) 的场景设计。
核心特性 特性 说明 键必须是对象 不能使用字符串、数字等原始值作键。 值可以是任意类型 包括原始值、对象、函数等。 弱引用键 键对象如果没有其他引用,会被 GC 自动回收。 不可迭代 没有 .size、.clear()、.keys()、for...of 等方法。 私有性 无法枚举或查看内部内容(设计如此)。
核心思想 :'我想给这个对象附加一些数据,但我不应该影响它的生命周期。'
基本用法
wm.set ('string' , 1 );
wm.set (42 , 'value' );
wm.size ;
wm.clear ();
[...wm];
const wm = new WeakMap ();
const obj1 = {};
const obj2 = { id : 1 };
wm.set (obj1, 'metadata for obj1' );
wm.set (obj2, { count : 42 });
console .log (wm.get (obj1));
console .log (wm.has (obj2));
wm.delete (obj1);
典型应用场景
不在对象上挂 _private 属性。
实例销毁时,私有数据自动消失。
const cache = new WeakMap ();
function expensiveCalculation (obj ) {
if (cache.has (obj)) {
return cache.get (obj);
}
const result = ;
cache.set (obj, result);
return result;
}
const data = { values : [...] };
expensiveCalculation (data);
const domMetadata = new WeakMap ();
function attachHandler (el ) {
if (domMetadata.has (el)) return ;
const state = { clickCount : 0 };
domMetadata.set (el, state);
el.addEventListener ('click' , () => {
state.clickCount ++;
console .log ('Clicked' , state.clickCount , 'times' );
});
}
const privateData = new WeakMap ();
class User {
constructor (name, ssn ) {
this .name = name;
privateData.set (this , { ssn });
}
getSSN ( ) {
return privateData.get (this ).ssn ;
}
}
const user = new User ('Alice' , '123-45-6789' );
console .log (user.getSSN ());
WeakMap vs Map 特性 MapWeakMap键的类型 任意类型 仅对象 引用类型 强引用(阻止 GC) 弱引用(不阻止 GC) 可遍历 是(.size, for...of 等) 否(完全不可遍历) 用途 通用键值存储 对象关联数据 + 自动内存管理
需要遍历/计数 → 用 Map。
只想'悄悄'给对象附加数据 → 用 WeakMap。
重要注意事项
弱引用 ≠ 立即回收 。
对象是否被回收取决于 GC 时机。
即使 WeakMap 是唯一引用,也可能暂时未被回收。
不能用于原始值作键 。
const wm = new WeakMap ();
wm.set ('id-123' , userData);
无法知道 WeakMap 里有什么 。
const wm = new WeakMap ();
wm.set ({}, 'secret' );
高级技巧:结合 FinalizationRegistry(谨慎使用) const registry = new FinalizationRegistry ((heldValue ) => {
console .log (`对象 ${heldValue} 已被回收` );
});
const wm = new WeakMap ();
const obj = { id : 1 };
wm.set (obj, 'data' );
registry.register (obj, 'Object with id=1' );
obj = null ;
FinalizationRegistry 是实验性 API,不推荐常规使用。
总结
你只能通过原对象 查笔记。
对象消失了,笔记自动焚毁。
没人能偷看你的笔记本内容.
键是对象 (不能是字符串/数字)。
存私有数据 (不污染对象)。
免内存泄漏 (随对象自动清理)。
别想遍历 (根本做不到)。
别问大小 (没有 .size)。
相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
Gemini 图片去水印 基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online