跳到主要内容
JavaScript 集合 (Set、WeakSet) 与映射 (Map、WeakMap) | 极客日志
JavaScript Node.js 大前端
JavaScript 集合 (Set、WeakSet) 与映射 (Map、WeakMap) 综述由AI生成 JavaScript ES6 引入的四种数据结构:Set、WeakSet、Map 和 WeakMap。Set 用于存储唯一值,支持去重和集合运算;WeakSet 仅存对象且为弱引用,不阻止垃圾回收。Map 是键值对集合,支持任意类型键并保持插入顺序;WeakMap 仅以对象为键,适合存储私有数据或元数据而不影响对象生命周期。文章对比了它们与普通对象的区别,提供了常用 API、遍历方法及典型应用场景,帮助开发者根据需求选择合适的数据结构。
清心 发布于 2026/3/30 更新于 2026/5/23 33 浏览一、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。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. 注意事项
Set不支持 直接获取第 n 个元素(无 .get(index) 方法)。
Set 无法序列化为 JSON(需先转数组):
JSON .stringify ([...mySet]);
new Set ([{ a : 1 }, { a : 1 }]).size ;
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) 。键的比较使用 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);
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)。
相关免费在线工具 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
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online