2025-12-25-ES6-关于JavaScript的Set 方法
2024 个字
10 分钟
2025-12-25-ES6-关于JavaScript的Set 方法
JavaScript Set 方法详解
1. Set 是什么?
Set 是 ES6 引入的一种新的数据结构,它类似于数组,但成员的值都是唯一的,没有重复的值。
// 创建一个 Setconst mySet = new Set();
// 或者从数组创建const setFromArray = new Set([1, 2, 3, 3, 4]); // 会自动去重console.log(setFromArray); // Set(4) {1, 2, 3, 4}2. Set 的基本方法
2.1 添加元素:add()
const set = new Set();
// 添加单个元素set.add(1);set.add(2);set.add(2); // 重复添加,不会被添加进去
// 链式调用set.add(3).add(4).add(5);
// 可以添加任意类型的值set.add("hello");set.add({ name: "John" });set.add([1, 2, 3]);set.add(NaN); // NaN 在 Set 中也是唯一的set.add(NaN); // 不会添加第二个 NaN
console.log(set.size); // 查看大小2.2 删除元素:delete()
const set = new Set([1, 2, 3, 4, 5]);
// 删除指定值set.delete(3); // 删除 3console.log(set); // Set(4) {1, 2, 4, 5}
// 删除不存在的值返回 falseconsole.log(set.delete(10)); // false
// 删除对象引用需要相同的引用const obj = { name: "John" };set.add(obj);console.log(set.delete({ name: "John" })); // false,因为不是同一个引用console.log(set.delete(obj)); // true,删除成功2.3 检查存在:has()
const set = new Set([1, 2, 3, 4, 5]);
console.log(set.has(2)); // trueconsole.log(set.has(10)); // false
// 对于 NaN,Set 能正确处理set.add(NaN);console.log(set.has(NaN)); // true2.4 清空 Set:clear()
const set = new Set([1, 2, 3, 4, 5]);console.log(set.size); // 5
set.clear();console.log(set.size); // 0console.log(set); // Set(0) {}3. Set 的遍历方法
3.1 forEach()
const set = new Set(["apple", "banana", "orange"]);
// 遍历每个元素set.forEach((value, key, set) => { // Set 的 key 和 value 相同 console.log(`${key}: ${value}`);});
// 输出:// apple: apple// banana: banana// orange: orange3.2 keys()、values()、entries()
const set = new Set(["a", "b", "c"]);
// keys() - 返回键名的遍历器for (let key of set.keys()) { console.log(key); // a, b, c}
// values() - 返回键值的遍历器(Set 的键和值相同)for (let value of set.values()) { console.log(value); // a, b, c}
// entries() - 返回键值对的遍历器for (let entry of set.entries()) { console.log(entry); // ['a', 'a'], ['b', 'b'], ['c', 'c']}4. Set 的特性
4.1 唯一性
// 自动去重const arr = [1, 2, 2, 3, 3, 3, 4, 5];const uniqueSet = new Set(arr);console.log([...uniqueSet]); // [1, 2, 3, 4, 5]
// 对象引用不同,不算重复const obj1 = { id: 1 };const obj2 = { id: 1 };const objSet = new Set([obj1, obj2, obj1]);console.log(objSet.size); // 2,obj1 和 obj2 被认为是不同的4.2 与数组的对比
// 查找元素const arr = [1, 2, 3, 4, 5];const set = new Set(arr);
// 数组查找是 O(n)console.log(arr.includes(3)); // true,需要遍历
// Set 查找是 O(1)console.log(set.has(3)); // true,哈希查找,更快
// 判断是否重复const hasDuplicates = (array) => { return new Set(array).size !== array.length;};
console.log(hasDuplicates([1, 2, 3, 3])); // trueconsole.log(hasDuplicates([1, 2, 3, 4])); // false4.3 迭代顺序
// Set 的遍历顺序就是插入顺序const set = new Set();set.add(3);set.add(1);set.add(2);
for (let item of set) { console.log(item); // 3, 1, 2(插入顺序)}
// 与对象不同,对象的键顺序不保证const obj = { 3: "a", 1: "b", 2: "c" };console.log(Object.keys(obj)); // ['1', '2', '3'](数字键会排序)5. 实际应用场景
5.1 数组去重(最常用)
// 传统方法function uniqueArray(arr) { return [...new Set(arr)];}
// 使用const numbers = [1, 2, 3, 3, 4, 4, 5];console.log(uniqueArray(numbers)); // [1, 2, 3, 4, 5]
// 字符串去重const str = "hello world";const uniqueChars = [...new Set(str)].join("");console.log(uniqueChars); // 'helo wrd'5.2 求交集、并集、差集
const setA = new Set([1, 2, 3, 4]);const setB = new Set([3, 4, 5, 6]);
// 并集const union = new Set([...setA, ...setB]);console.log([...union]); // [1, 2, 3, 4, 5, 6]
// 交集const intersection = new Set([...setA].filter((x) => setB.has(x)));console.log([...intersection]); // [3, 4]
// 差集(A 有 B 没有)const difference = new Set([...setA].filter((x) => !setB.has(x)));console.log([...difference]); // [1, 2]5.3 数据筛选
// 从数组中过滤出唯一的元素const data = [ { id: 1, name: "Alice" }, { id: 2, name: "Bob" }, { id: 1, name: "Alice" }, // 重复 { id: 3, name: "Charlie" },];
// 基于 id 去重const seen = new Set();const uniqueData = data.filter((item) => { if (seen.has(item.id)) { return false; } else { seen.add(item.id); return true; }});
console.log(uniqueData);// [// { id: 1, name: 'Alice' },// { id: 2, name: 'Bob' },// { id: 3, name: 'Charlie' }// ]5.4 标签/分类系统
class TagSystem { constructor() { this.tags = new Set(); }
addTag(tag) { this.tags.add(tag.toLowerCase()); return this; }
removeTag(tag) { this.tags.delete(tag.toLowerCase()); return this; }
hasTag(tag) { return this.tags.has(tag.toLowerCase()); }
getAllTags() { return [...this.tags]; }
merge(otherTagSystem) { otherTagSystem.tags.forEach((tag) => this.tags.add(tag)); return this; }}
// 使用const articleTags = new TagSystem();articleTags.addTag("JavaScript").addTag("Tutorial").addTag("JavaScript"); // 不会重复添加
console.log(articleTags.getAllTags()); // ['javascript', 'tutorial']6. Set 性能优势
// 测试查找性能const largeArray = Array.from({ length: 1000000 }, (_, i) => i);const largeSet = new Set(largeArray);
console.time("Array查找");largeArray.includes(999999);console.timeEnd("Array查找"); // 约 0.5-1ms
console.time("Set查找");largeSet.has(999999);console.timeEnd("Set查找"); // 约 0.01ms
// 测试重复检查性能const checkDuplicatesArray = (arr) => { for (let i = 0; i < arr.length; i++) { for (let j = i + 1; j < arr.length; j++) { if (arr[i] === arr[j]) return true; } } return false;};
const checkDuplicatesSet = (arr) => { return new Set(arr).size !== arr.length;};
const testArray = Array.from({ length: 10000 }, () => Math.floor(Math.random() * 1000));
console.time("数组检查重复");checkDuplicatesArray(testArray);console.timeEnd("数组检查重复"); // 慢
console.time("Set检查重复");checkDuplicatesSet(testArray);console.timeEnd("Set检查重复"); // 快7. WeakSet
7.1 与 Set 的区别
// Set 可以存储任何类型的值const set = new Set();let obj = { name: "John" };set.add(obj);console.log(set.size); // 1
// WeakSet 只能存储对象引用const weakSet = new WeakSet();weakSet.add(obj); // OK// weakSet.add(1); // TypeError: Invalid value used in weak set
// WeakSet 的引用是弱引用,不会阻止垃圾回收obj = null; // 清除引用// 等待垃圾回收后,weakSet 中的对应项会自动被移除7.2 WeakSet 的方法
const weakSet = new WeakSet();const obj1 = {};const obj2 = {};
weakSet.add(obj1);weakSet.add(obj2);
console.log(weakSet.has(obj1)); // trueweakSet.delete(obj1);console.log(weakSet.has(obj1)); // false
// WeakSet 没有 size 属性,不能遍历// console.log(weakSet.size); // undefined// weakSet.forEach(...) // 没有 forEach 方法7.3 WeakSet 的应用场景
// 1. 存储 DOM 节点,避免内存泄漏const weakSet = new WeakSet();
document.querySelectorAll("button").forEach((button) => { weakSet.add(button); button.addEventListener("click", () => { if (weakSet.has(button)) { console.log("按钮在集合中"); } });});
// 当按钮从 DOM 中移除时,会被自动垃圾回收// weakSet 中的引用也会自动移除
// 2. 私有属性模拟const privateData = new WeakSet();
class User { constructor(name) { this.name = name; privateData.add(this); // 标记为"初始化" }
isInitialized() { return privateData.has(this); }}
const user = new User("Alice");console.log(user.isInitialized()); // true8. 常见的一些陷阱和注意事项
8.1 NaN 的处理
const set = new Set();set.add(NaN);console.log(set.has(NaN)); // true
// 注意:NaN === NaN 为 false,但 Set 认为 NaN 等于自身8.2 对象引用
const set = new Set();const obj1 = { id: 1 };const obj2 = { id: 1 };
set.add(obj1);set.add(obj2);
console.log(set.size); // 2,因为 obj1 和 obj2 是不同的对象引用console.log(set.has({ id: 1 })); // false,新对象不是同一个引用8.3 类型转换
const set = new Set();set.add(1);set.add("1");
console.log(set.size); // 2,1 和 '1' 类型不同,不会去重console.log(set.has(1)); // trueconsole.log(set.has("1")); // true8.4 遍历时修改
const set = new Set([1, 2, 3]);
// 在遍历时删除元素for (let item of set) { if (item === 2) { set.delete(item); // 当前正在遍历的元素可以安全删除 } console.log(item); // 1, 2, 3(会正常遍历完)}
// 在遍历时添加元素可能有问题for (let item of set) { if (item === 1) { set.add(4); // 添加的元素在本次遍历中可能不会被访问 }}9. 与其它数据结构的转换
9.1 Set 与 Array
// Set → Arrayconst set = new Set([1, 2, 3]);const arr1 = Array.from(set);const arr2 = [...set];console.log(arr1, arr2); // [1, 2, 3]
// Array → Setconst array = [1, 2, 2, 3, 3, 3];const newSet = new Set(array);console.log([...newSet]); // [1, 2, 3]9.2 Set 与 String
// 字符串去重并排序const str = "javascript";const uniqueSorted = [...new Set(str)].sort().join("");console.log(uniqueSorted); // 'aijprstv'9.3 Set 与 Map
// 使用 Set 存储 Map 的键const map = new Map([ ["a", 1], ["b", 2], ["c", 3],]);
const keysSet = new Set(map.keys());console.log([...keysSet]); // ['a', 'b', 'c']10. 综合示例
// 实现一个简单的权限系统class PermissionSystem { constructor() { this.permissions = new Set(); }
// 添加权限 addPermission(permission) { this.permissions.add(permission); return this; }
// 批量添加权限 addPermissions(permissionsArray) { permissionsArray.forEach((p) => this.permissions.add(p)); return this; }
// 检查是否有某个权限 hasPermission(permission) { return this.permissions.has(permission); }
// 检查是否有所有指定权限 hasAllPermissions(requiredPermissions) { return requiredPermissions.every((p) => this.permissions.has(p)); }
// 检查是否有任一指定权限 hasAnyPermission(permissions) { return permissions.some((p) => this.permissions.has(p)); }
// 获取所有权限 getAllPermissions() { return [...this.permissions]; }
// 移除权限 removePermission(permission) { this.permissions.delete(permission); return this; }
// 清空所有权限 clearPermissions() { this.permissions.clear(); return this; }}
// 使用示例const userPermissions = new PermissionSystem();userPermissions .addPermissions(["read", "write", "delete"]) .addPermission("execute");
console.log(userPermissions.hasPermission("write")); // trueconsole.log(userPermissions.hasAllPermissions(["read", "write"])); // trueconsole.log(userPermissions.getAllPermissions()); // ['read', 'write', 'delete', 'execute']总之
- 高效查找:
has()方法的时间复杂度是 O(1) - 遍历时按插入顺序输出
- 需要去重时,优先考虑 Set
- 需要快速查找元素是否存在时,用 Set 代替数组
- 存储唯一值集合时,Set 是最佳选择
- 需要存储对象引用并自动清理时,考虑 WeakSet
分享到社交平台
将本文分享给你的朋友们
2025-12-25-ES6-关于JavaScript的Set 方法
https://firefly.cuteleaf.cn/posts/2025-12-25-关于javascript的set方法/ 相关文章 智能推荐
1
2025-11-16-ES6相关
前端 2025-11-16
2
2025-11-25 关于链表(Javascript)
数据结构 2025-11-25
3
2025-11-04-JavaScript 常用方法速查
前端 2025-11-04
4
2025-12-26-JavaScript 数字,数组,字符串的处理
算法 2025-12-26
5
2025-12-25-力扣百题速练(Javascript/TypeScript)Vol.1
力扣 2025-12-25
随机文章 随便看看
Zhongye