JavaScript Set 方法详解
1. Set 是什么?
Set 是 ES6 引入的一种新的数据结构,它类似于数组,但成员的值都是唯一的,没有重复的值。
1 2 3 4 5 6
| const mySet = new Set();
const setFromArray = new Set([1, 2, 3, 3, 4]); console.log(setFromArray);
|
2. Set 的基本方法
2.1 添加元素:add()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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); set.add(NaN);
console.log(set.size);
|
2.2 删除元素:delete()
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const set = new Set([1, 2, 3, 4, 5]);
set.delete(3); console.log(set);
console.log(set.delete(10));
const obj = { name: "John" }; set.add(obj); console.log(set.delete({ name: "John" })); console.log(set.delete(obj));
|
2.3 检查存在:has()
1 2 3 4 5 6 7 8
| const set = new Set([1, 2, 3, 4, 5]);
console.log(set.has(2)); console.log(set.has(10));
set.add(NaN); console.log(set.has(NaN));
|
2.4 清空 Set:clear()
1 2 3 4 5 6
| const set = new Set([1, 2, 3, 4, 5]); console.log(set.size);
set.clear(); console.log(set.size); console.log(set);
|
3. Set 的遍历方法
3.1 forEach()
1 2 3 4 5 6 7 8 9 10 11 12
| const set = new Set(["apple", "banana", "orange"]);
set.forEach((value, key, set) => { console.log(`${key}: ${value}`); });
|
3.2 keys()、values()、entries()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const set = new Set(["a", "b", "c"]);
for (let key of set.keys()) { console.log(key); }
for (let value of set.values()) { console.log(value); }
for (let entry of set.entries()) { console.log(entry); }
|
4. Set 的特性
4.1 唯一性
1 2 3 4 5 6 7 8 9 10
| const arr = [1, 2, 2, 3, 3, 3, 4, 5]; const uniqueSet = new Set(arr); console.log([...uniqueSet]);
const obj1 = { id: 1 }; const obj2 = { id: 1 }; const objSet = new Set([obj1, obj2, obj1]); console.log(objSet.size);
|
4.2 与数组的对比
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const arr = [1, 2, 3, 4, 5]; const set = new Set(arr);
console.log(arr.includes(3));
console.log(set.has(3));
const hasDuplicates = (array) => { return new Set(array).size !== array.length; };
console.log(hasDuplicates([1, 2, 3, 3])); console.log(hasDuplicates([1, 2, 3, 4]));
|
4.3 迭代顺序
1 2 3 4 5 6 7 8 9 10 11 12 13
| const set = new Set(); set.add(3); set.add(1); set.add(2);
for (let item of set) { console.log(item); }
const obj = { 3: "a", 1: "b", 2: "c" }; console.log(Object.keys(obj));
|
5. 实际应用场景
5.1 数组去重(最常用)
1 2 3 4 5 6 7 8 9 10 11 12 13
| function uniqueArray(arr) { return [...new Set(arr)]; }
const numbers = [1, 2, 3, 3, 4, 4, 5]; console.log(uniqueArray(numbers));
const str = "hello world"; const uniqueChars = [...new Set(str)].join(""); console.log(uniqueChars);
|
5.2 求交集、并集、差集
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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]);
const intersection = new Set([...setA].filter((x) => setB.has(x))); console.log([...intersection]);
const difference = new Set([...setA].filter((x) => !setB.has(x))); console.log([...difference]);
|
5.3 数据筛选
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| const data = [ { id: 1, name: "Alice" }, { id: 2, name: "Bob" }, { id: 1, name: "Alice" }, { id: 3, name: "Charlie" }, ];
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);
|
5.4 标签/分类系统
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| 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());
|
6. Set 性能优势
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| const largeArray = Array.from({ length: 1000000 }, (_, i) => i); const largeSet = new Set(largeArray);
console.time("Array查找"); largeArray.includes(999999); console.timeEnd("Array查找");
console.time("Set查找"); largeSet.has(999999); console.timeEnd("Set查找");
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 的区别
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const set = new Set(); let obj = { name: "John" }; set.add(obj); console.log(set.size);
const weakSet = new WeakSet(); weakSet.add(obj);
obj = null;
|
7.2 WeakSet 的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const weakSet = new WeakSet(); const obj1 = {}; const obj2 = {};
weakSet.add(obj1); weakSet.add(obj2);
console.log(weakSet.has(obj1)); weakSet.delete(obj1); console.log(weakSet.has(obj1));
|
7.3 WeakSet 的应用场景
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| const weakSet = new WeakSet();
document.querySelectorAll("button").forEach((button) => { weakSet.add(button); button.addEventListener("click", () => { if (weakSet.has(button)) { console.log("按钮在集合中"); } }); });
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());
|
8. 常见的一些陷阱和注意事项
8.1 NaN 的处理
1 2 3 4 5
| const set = new Set(); set.add(NaN); console.log(set.has(NaN));
|
8.2 对象引用
1 2 3 4 5 6 7 8 9
| const set = new Set(); const obj1 = { id: 1 }; const obj2 = { id: 1 };
set.add(obj1); set.add(obj2);
console.log(set.size); console.log(set.has({ id: 1 }));
|
8.3 类型转换
1 2 3 4 5 6 7
| const set = new Set(); set.add(1); set.add("1");
console.log(set.size); console.log(set.has(1)); console.log(set.has("1"));
|
8.4 遍历时修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const set = new Set([1, 2, 3]);
for (let item of set) { if (item === 2) { set.delete(item); } console.log(item); }
for (let item of set) { if (item === 1) { set.add(4); } }
|
9. 与其它数据结构的转换
9.1 Set 与 Array
1 2 3 4 5 6 7 8 9 10
| const set = new Set([1, 2, 3]); const arr1 = Array.from(set); const arr2 = [...set]; console.log(arr1, arr2);
const array = [1, 2, 2, 3, 3, 3]; const newSet = new Set(array); console.log([...newSet]);
|
9.2 Set 与 String
1 2 3 4
| const str = "javascript"; const uniqueSorted = [...new Set(str)].sort().join(""); console.log(uniqueSorted);
|
9.3 Set 与 Map
1 2 3 4 5 6 7 8 9
| const map = new Map([ ["a", 1], ["b", 2], ["c", 3], ]);
const keysSet = new Set(map.keys()); console.log([...keysSet]);
|
10. 综合示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| 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")); console.log(userPermissions.hasAllPermissions(["read", "write"])); console.log(userPermissions.getAllPermissions());
|
总之
- 高效查找:
has()方法的时间复杂度是 O(1)
- 遍历时按插入顺序输出
- 需要去重时,优先考虑 Set
- 需要快速查找元素是否存在时,用 Set 代替数组
- 存储唯一值集合时,Set 是最佳选择
- 需要存储对象引用并自动清理时,考虑 WeakSet