TypeScript Set 和 WeakMap
TypeScript 继承自 JavaScript 的 Set 和 WeakMap 数据结构,提供了更强大的类型支持。
这些数据结构在处理唯一值集合、键值对映射、缓存等场景非常有用。
为什么需要 Set 和 WeakMap
在开发中,我们经常需要处理唯一值集合和键值对映射。
Set 提供了自动去重的集合功能,比数组更方便处理唯一值。
WeakSet 和 WeakMap 使用弱引用,不会阻止垃圾回收,适用于需要避免内存泄漏的场景,如缓存 DOM 节点。
概念说明:Set 是值的集合,值唯一;Map 是键值对集合,键可以是任意类型。WeakSet 和 WeakMap 使用弱引用,不影响垃圾回收。
Set
Set 是值的集合,值唯一,不允许重复。
实例
var numbers = new Set<number>();
// 添加元素
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(1); // 重复值会被忽略,不会添加
// 检查大小和包含
console.log("Set 大小: " + numbers.size);
console.log("是否包含 2: " + numbers.has(2));
// 遍历 Set
numbers.forEach(function(value) {
console.log("值: " + value);
});
// 转换为数组
var arr = Array.from(numbers);
console.log("转换为数组: " + arr);
运行结果:
Set 大小: 3 是否包含 2: true 值: 1 值: 2 值: 3 转换为数组: 1,2,3
去重:Set 会自动忽略重复的值,这使得它非常适合用于数组去重。
Set 类型注解
可以显式指定 Set 中值的类型。
实例
// 只能添加字符串类型的值
var stringSet: Set<string> = new Set();
stringSet.add("a");
stringSet.add("b");
// 对象 Set
// 定义 Person 接口
interface Person {
name: string;
}
// 创建存储 Person 对象的 Set
var personSet: Set<Person> = new Set();
personSet.add({ name: "Alice" });
personSet.add({ name: "Bob" });
console.log("字符串 Set: " + Array.from(stringSet));
console.log("对象 Set 大小: " + personSet.size);
泛型:使用
Set<T>语法指定 Set 中元素的类型。
WeakSet
WeakSet 存储对象引用,引用为弱引用(不影响垃圾回收)。
实例
var weakSet = new WeakSet();
// 创建对象
var obj1 = { name: "Alice" };
var obj2 = { name: "Bob" };
// 添加对象到 WeakSet
weakSet.add(obj1);
weakSet.add(obj2);
// 检查是否包含
console.log("是否包含 obj1: " + weakSet.has(obj1));
// 移除引用后,对象可能被垃圾回收
weakSet.delete(obj1);
console.log("删除后是否包含 obj1: " + weakSet.has(obj1));
注意:WeakSet 不能遍历,类型注解只能是
object。这使得 WeakSet 适合存储需要被垃圾回收的对象。
Map
Map 是键值对集合,键可以是任意类型。
实例
var map = new Map<string, number>();
// 设置键值对
map.set("one", 1);
map.set("two", 2);
map.set("three", 3);
// 获取值
console.log("获取 two: " + map.get("two"));
console.log("Map 大小: " + map.size);
console.log("是否包含 three: " + map.has("three"));
// 遍历 Map
map.forEach(function(value, key) {
console.log(key + ": " + value);
});
// 转换为数组
console.log("转换为数组: " + Array.from(map.entries()));
运行结果:
获取 two: 2 Map 大小: 3 是否包含 three: true one: 1 two: 2 three: 3 转换为数组: one,1,two,2,three,3
优势:Map 的键可以是任意类型(对象、函数等),这比使用对象作为键更灵活。
WeakMap
WeakMap 的键是弱引用,不影响垃圾回收。
实例
// 键类型为 object,值类型为 string
var weakMap = new WeakMap<object, string>();
// 创建对象作为键
var keyObj = { id: 1 };
// 设置键值对
weakMap.set(keyObj, "value1");
// 获取值
console.log("获取值: " + weakMap.get(keyObj));
console.log("是否包含: " + weakMap.has(keyObj));
// 删除键值对
weakMap.delete(keyObj);
console.log("删除后: " + weakMap.has(keyObj));
应用场景:WeakMap 常用于缓存 DOM 节点数据,当 DOM 节点被移除时,缓存数据也会被自动清理,避免内存泄漏。
实际应用场景
使用 Map 统计数组元素出现次数。
实例
function countElements(arr: string[]): Map<string, number> {
// 创建 Map,键是字符串,值是数字
var counts = new Map<string, number>();
// 遍历数组
for (var _i = 0, arr_1 = arr; _i < arr_1.length; _i++) {
var item = arr_1[_i];
// 获取当前计数,如果没有则返回 0
var currentCount = counts.get(item) || 0;
// 更新计数
counts.set(item, currentCount + 1);
}
return counts;
}
// 测试
var fruits = ["apple", "banana", "apple", "orange", "banana", "apple"];
var result = countElements(fruits);
// 遍历结果
result.forEach(function(count, fruit) {
console.log(fruit + ": " + count);
});
运行结果:
apple: 3 banana: 2 orange: 1
实用:Map 是实现缓存、统计、索引等功能的理想选择。
注意事项
- Set 唯一性:Set 自动忽略重复值
- WeakSet/WeakMap:键必须是对象,不能遍历
- Map 键类型:Map 的键可以是任意类型
- 内存管理:WeakSet/WeakMap 不阻止垃圾回收
选择建议:需要唯一值集合用 Set,需要键值映射用 Map,需要避免内存泄漏用 WeakSet/WeakMap。
总结
Set 和 Map 是 TypeScript 中非常有用的数据结构。
- Set:值的集合,值唯一,自动去重
- WeakSet:对象弱引用集合,不可遍历,适用于需要被垃圾回收的场景
- Map:键值对集合,键可以是任意类型
- WeakMap:键弱引用,不可遍历,适用于缓存和私有数据
建议:根据具体需求选择合适的数据结构:需要去重用 Set,需要映射用 Map,需要内存优化用 Weak 版本。
点我分享笔记