目录
WeakSet 详解
WeakSet 是 JavaScript 中的一种集合类型,与普通的 Set 类似,但有一些重要的区别。下面我将详细讲解 WeakSet 的特性、用法以及与 Set 的区别
基本概念
WeakSet 是一种特殊的集合,它只能存储对象引用,不能存储原始值(如数字、字符串、布尔值等)。与 Set 不同,WeakSet 中的对象是弱引用的,这意味着如果没有其他引用指向这些对象,它们可以被垃圾回收机制自动回收。
创建 WeakSet
const weakSet = new WeakSet();
const obj1 = {};
const obj2 = {};
// 添加对象到 WeakSet
weakSet.add(obj1);
weakSet.add(obj2);
console.log(weakSet.has(obj1)); // true
WeakSet 的主要方法
-
add(value): 向 WeakSet 添加一个对象
-
delete(value): 从 WeakSet 中移除指定的对象
-
has(value): 检查 WeakSet 中是否包含指定的对象
注意:WeakSet 没有 size 属性,也没有 clear() 方法,也不能被遍历(没有 forEach、keys、values 等方法)
WeakSet 的特性
1.只能存储对象引用
const weakSet = new WeakSet();
weakSet.add(1); // TypeError: Invalid value used in weak set
weakSet.add("string"); // TypeError: Invalid value used in weak set
2.弱引用特性
-
如果 WeakSet 中的对象没有其他引用,它会被垃圾回收
-
这使得 WeakSet 适合用于存储临时对象或私有数据
3.不可枚举
-
无法获取 WeakSet 中的所有值
-
没有 size 属性,无法知道集合中有多少元素
WeakSet 的使用场景
1. 避免内存泄漏(DOM 元素管理)
场景:跟踪 DOM 元素是否被处理过
const processedElements = new WeakSet();
document.querySelectorAll(".btn").forEach(btn => {
processedElements.add(btn);
btn.addEventListener("click", () => {
if (processedElements.has(btn)) {
// 确保只处理一次
}
});
});
// 当 DOM 元素被移除时,WeakSet 自动清理引用
2. 临时缓存系统
场景:缓存计算结果,当对象不再使用时自动失效
const cache = new WeakSet();
function heavyCalculation(obj) {
if (cache.has(obj)) {
return "结果来自缓存";
}
const result = /* 复杂计算 */;
cache.add(obj);
return "结果来自计算";
}
3. 私有属性模拟
场景:标记对象是否已初始化(ES6 Class)
const initialized = new WeakSet();
class SecureAPI {
constructor() {
initialized.add(this);
}
fetchData() {
if (!initialized.has(this)) {
throw new Error("必须先初始化实例");
}
// 安全执行逻辑
}
}
4. 防止循环引用
场景:深度拷贝时记录已处理对象
function deepClone(obj, visited = new WeakSet()) {
if (typeof obj !== "object" || obj === null) return obj;
if (visited.has(obj)) return null; // 避免循环引用
visited.add(obj);
// 继续克隆逻辑...
}
与其他数据结构的对比
| 特性 | WeakSet | Set | WeakMap |
|---|---|---|---|
| 键类型 | 对象 | 任意值 | 对象 |
| 值类型 | 无(仅存储对象引用) | 任意值 | 任意值 |
| 垃圾回收 | 自动清理无引用的键 | 不自动清理 | 自动清理无引用的键 |
| 遍历能力 | ❌ 不可遍历 | ✅ 可遍历 | ❌ 不可遍历 |
| 典型场景 | 临时标记、防内存泄漏 | 通用集合操作 | 对象关联元数据 |
1. WeakSet 没有实例属性
-
没有
size属性
Set有size属性可以获取元素数量,但 WeakSet 没有类似属性。
原因:WeakSet 的元素是弱引用,垃圾回收可能随时移除元素,无法保证size的准确性。 -
没有迭代器属性
Set支持keys(),values(),entries()等迭代方法,但 WeakSet 不支持遍历。
2. WeakSet 仅有的方法
WeakSet 只有以下 3 个核心方法(无属性):
| 方法名 | 作用 | 示例 |
|---|---|---|
add(value) | 添加对象到 WeakSet | weakSet.add(obj) |
has(value) | 检查对象是否存在 | weakSet.has(obj) → true |
delete(value) | 删除指定对象 | weakSet.delete(obj) |
3. 原型链属性
通过 Object.getOwnPropertyNames(WeakSet.prototype) 可以看到,WeakSet 的原型链上只有方法,没有属性:
console.log(Object.getOwnPropertyNames(WeakSet.prototype));
// 输出: ["constructor", "add", "delete", "has"]
WeakSet 注意事项
-
WeakSet 不可迭代,因此不能使用
for...of循环 -
不能查看 WeakSet 中的所有内容,只能检查特定对象是否存在
-
主要用于存储额外的对象信息,而不是作为主要的数据存储结构
WeakSet 最佳实践建议
-
优先用于临时性数据:WeakSet 适合存储短期存在的对象关联信息。
-
结合内存分析工具:使用 Chrome DevTools 的 Memory 面板验证内存管理效果。
-
避免替代普通 Set:需要统计数量或遍历时,必须使用普通 Set。
-
类型安全检查:添加对象前验证类型:
function addToWeakSet(ws, obj) { if (typeof obj !== "object" || obj === null) { throw new TypeError("WeakSet 只接受对象"); } ws.add(obj); }
完整HTML示例代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WeakSet 完整方法演示</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 20px auto;
padding: 20px;
}
button {
margin: 5px;
padding: 8px 12px;
background: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
#output {
margin-top: 20px;
padding: 15px;
background: #f5f5f5;
border-radius: 4px;
}
.obj-id {
color: #2196F3;
font-weight: bold;
}
</style>
</head>
<body>
<h2>WeakSet 方法演示</h2>
<div>
<button onclick="createObject()">1. 创建新对象</button>
<button onclick="addToWeakSet()">2. 添加到 WeakSet</button>
<button onclick="checkInWeakSet()">3. 检查是否存在</button>
<button onclick="deleteFromWeakSet()">4. 删除对象</button>
</div>
<div id="output"></div>
<script>
const weakSet = new WeakSet();
let currentObject = null;
// 用于跟踪对象ID
let objectCounter = 0;
function createObject() {
currentObject = { id: `obj_${++objectCounter}` };
log(`创建新对象: <span class="obj-id">${currentObject.id}</span>`);
}
function addToWeakSet() {
if (!currentObject) return;
weakSet.add(currentObject);
log(`已添加对象 <span class="obj-id">${currentObject.id}</span> 到 WeakSet`);
}
function checkInWeakSet() {
if (!currentObject) return;
const exists = weakSet.has(currentObject);
log(`对象 <span class="obj-id">${currentObject.id}</span> 在 WeakSet 中: ${exists}`);
}
function deleteFromWeakSet() {
if (!currentObject) return;
const deleted = weakSet.delete(currentObject);
log(`删除对象 <span class="obj-id">${currentObject.id}</span>: ${deleted ? '成功' : '失败'}`);
}
function log(message) {
const output = document.getElementById('output');
output.innerHTML += `${message}<br>`;
output.scrollTop = output.scrollHeight;
}
</script>
</body>
</html>

1119

被折叠的 条评论
为什么被折叠?



