在日常编程中,我们经常需要处理数据集合。虽然数组(Array)很常用,但在某些情况下,使用 Set 会是更好的选择。Set 是 JavaScript 中的一种集合类型,它提供了一些独特的优势。
数组允许重复的值,而 Set 会自动去重:
// 数组 - 允许重复值
const arr = [1, 2, 2, 3, 3, 3];
console.log(arr); // [1, 2, 2, 3, 3, 3]
// Set - 自动去重
const set = new Set([1, 2, 2, 3, 3, 3]);
console.log([...set]); // [1, 2, 3]在处理大量数据时,Set 在查找和添加操作上通常更快:
const largeArray = Array.from({length: 10000}, (_, i) => i);
const largeSet = new Set(largeArray);
// 查找操作对比
console.time('Array includes');
largeArray.includes(9999);
console.timeEnd('Array includes'); // 大约 0.1ms
console.time('Set has');
largeSet.has(9999);
console.timeEnd('Set has'); // 大约 0.01ms
// 添加操作对比
console.time('Array push');
largeArray.push(10000);
console.timeEnd('Array push'); // 大约 0.01ms
console.time('Set add');
largeSet.add(10000);
console.timeEnd('Set add'); // 大约 0.005msSet 让数组去重变得非常简单:
// 传统去重方法比较麻烦
// 使用 Set 可以一行代码完成
const arr = [1, 2, 2, 3, 3, 4, 5, 5];
const uniqueArr = [...new Set(arr)];
console.log(uniqueArr); // [1, 2, 3, 4, 5]
// 封装成函数
const removeDuplicates = array => [...new Set(array)];Set 很适合进行数学上的集合运算:
// 并集:包含两个集合的所有元素
const union = (setA, setB) => new Set([...setA, ...setB]);
// 交集:只包含两个集合都有的元素
const intersection = (setA, setB) => new Set(
[...setA].filter(x => setB.has(x))
);
// 差集:包含在第一个集合但不在第二个集合的元素
const difference = (setA, setB) => new Set(
[...setA].filter(x => !setB.has(x))
);
// 使用示例
const setA = new Set([1, 2, 3]);
const setB = new Set([2, 3, 4]);
console.log([...union(setA, setB)]); // [1, 2, 3, 4]
console.log([...intersection(setA, setB)]); // [2, 3]
console.log([...difference(setA, setB)]); // [1]Set 可以方便地检查数据唯一性:
// 验证某个字段的值是否唯一
const validateUnique = (array, field) => {
const values = array.map(item => item[field]);
return values.length === new Set(values).size;
};
const users = [
{ id: 1, email: 'test@test.com' },
{ id: 2, email: 'test@test.com' }, // 重复邮箱
{ id: 3, email: 'other@test.com' }
];
console.log(validateUnique(users, 'email')); // falseclass TagManager {
constructor() {
this.tags = new Set();
}
addTags(newTags) {
newTags.forEach(tag => this.tags.add(tag.toLowerCase()));
}
removeTag(tag) {
this.tags.delete(tag.toLowerCase());
}
hasTag(tag) {
return this.tags.has(tag.toLowerCase());
}
getTags() {
return [...this.tags].sort();
}
mergeWith(otherTagManager) {
return new Set([...this.tags, ...otherTagManager.tags]);
}
}
// 使用示例
const manager = new TagManager();
manager.addTags(['JavaScript', 'Web', 'javascript', 'api']);
console.log(manager.getTags()); // ['api', 'javascript', 'web']class PermissionManager {
constructor() {
this.userPermissions = new Map(); // 用户ID -> 权限集合
}
grantPermission(userId, permission) {
if (!this.userPermissions.has(userId)) {
this.userPermissions.set(userId, new Set());
}
this.userPermissions.get(userId).add(permission);
}
revokePermission(userId, permission) {
const permissions = this.userPermissions.get(userId);
if (permissions) {
permissions.delete(permission);
}
}
hasPermission(userId, permission) {
const permissions = this.userPermissions.get(userId);
return permissions ? permissions.has(permission) : false;
}
getUserPermissions(userId) {
return [...(this.userPermissions.get(userId) || [])];
}
}
// 使用示例
const pm = new PermissionManager();
pm.grantPermission('user1', 'read');
pm.grantPermission('user1', 'write');
console.log(pm.hasPermission('user1', 'read')); // trueclass SimpleCache {
constructor(maxSize = 100) {
this.cache = new Map();
this.accessOrder = new Set();
this.maxSize = maxSize;
}
set(key, value) {
// 如果缓存已满,移除最久未使用的项
if (this.accessOrder.size >= this.maxSize) {
const firstKey = this.accessOrder.values().next().value;
this.accessOrder.delete(firstKey);
this.cache.delete(firstKey);
}
this.cache.set(key, value);
this.accessOrder.add(key);
}
get(key) {
if (this.cache.has(key)) {
// 更新访问顺序
this.accessOrder.delete(key);
this.accessOrder.add(key);
return this.cache.get(key);
}
return null;
}
}虽然 Set 有很多优点,但并不是所有情况都适合使用:
const arr = ['a', 'b', 'c'];
console.log(arr[1]); // 'b' - 数组支持索引访问
const set = new Set(['a', 'b', 'c']);
// set[1] 这样写是不行的,Set 不支持索引访问const arr = [1, 2, 3, 4, 5];
const doubled = arr.map(x => x * 2); // 很简单
const set = new Set([1, 2, 3, 4, 5]);
const doubledSet = new Set([...set].map(x => x * 2)); // 需要转换使用 Set 的情况:
需要确保值的唯一性
频繁检查某个值是否存在
进行集合运算(并集、交集、差集)
需要快速添加和删除元素
使用 Array 的情况:
需要保留重复元素
需要按索引访问元素
需要保持严格的元素顺序
需要频繁使用数组方法(map、filter 等)
需要与其他期望数组的 API 交互
Set 是 JavaScript 中一个很有用的数据结构。在合适的场景下使用 Set,可以提升代码的性能和可读性。关键是了解它的特性,在需要唯一性检查和快速查找时使用 Set,在其他情况下继续使用数组。
最好的做法是根据具体需求选择合适的数据结构。有时候,甚至可以将两者结合使用,发挥各自的优势。
本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!
箭头函数是ES6中非常重要的性特性。它最显著的作用就是:更简短的函数,并且不绑定this,arguments等属性,它的this永远指向其上下文的 this。它最适合用于非方法函数,并且它们不能用作构造函数。
js模块化的开发并不是随心所欲的,为了便于他人的使用和交流,需要遵循一定的规范。目前,通行的js模块规范主要有两种:CommonJS和AMD
ES6中添加了一个新属性解构,允许你使用类似数组或对象字面量的语法将数组和对象的属性赋给各种变量。用途:交换变量的值、从函数返回多个值、函数参数的定义、提取JSON数据、函数参数的默认值...
ES6中let变量的特点:1.let声明变量存在块级作用域,2.let不能先使用再声明3.暂时性死区,在代码块内使用let命令声明变量之前,该变量都是不可用的,4.不允许重复声明
ES6的7个实用技巧包括:1交换元素,2 调试,3 单条语句,4 数组拼接,5 制作副本,6 命名参数,7 Async/Await结合数组解构
ES6装饰器(Decorator)是一个函数,用来修改类的行为 在设计阶段可以对类和属性进行注释和修改。从本质上上讲,装饰器的最大作用是修改预定义好的逻辑,或者给各种结构添加一些元数据。
Query作为曾经Web前端的必备利器,随着MVVM框架的兴起,如今已稍显没落。用ES6写了一个基于class简化版的jQuery,包含基础DOM操作,支持链式操作...
ES6 中的一些技巧:模版字符串、块级作用域、Let、Const、块级作用域函数问题、扩展运算符、函数默认参数、解构、对象字面量和简明参数、动态属性名称、箭头函数、for … of 循环、数字字面量。
Rest/Spread 属性:rest操作符在对象解构中的使用。目前,该操作符仅适用于数组解构和参数定义。spread操作符在对象字面量中的使用。目前,这个操作符只能在数组字面量和函数以及方法调用中使用。
ES6使您的代码更具表现力和可读性。而且它与React完美配合!现在您已了解更多基础知识:现在是时候将你的ES6技能提升到一个新的水平!嵌套props解构、 传下所有props、props解构、作为参数的函数、列表解构
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!