news 2026/9/2 6:15:59

JavaScript 常用数组去重方法及性能对比(附:扩展运算符...总结)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JavaScript 常用数组去重方法及性能对比(附:扩展运算符...总结)

JavaScript 数组去重有多种方法,性能差异显著。

以下是常用方法及其性能对比:


1. ES6 Set(推荐)

javascript

// 最快,代码最简洁 function uniqueSet(arr) { return [...new Set(arr)]; // 或 Array.from(new Set(arr)) }

2. Filter + indexOf

javascript

function uniqueFilter(arr) { return arr.filter((item, index) => arr.indexOf(item) === index); }

3. Reduce

javascript

function uniqueReduce(arr) { return arr.reduce((acc, cur) => { if (!acc.includes(cur)) { acc.push(cur); } return acc; }, []); }

4. 双层循环

javascript

function uniqueDoubleLoop(arr) { const result = []; for (let i = 0; i < arr.length; i++) { let isDuplicate = false; for (let j = 0; j < result.length; j++) { if (arr[i] === result[j]) { isDuplicate = true; break; } } if (!isDuplicate) result.push(arr[i]); } return result; }

5. Object键值(仅字符串)

javascript

function uniqueObject(arr) { const obj = {}; const result = []; for (let i = 0; i < arr.length; i++) { if (!obj[arr[i]]) { obj[arr[i]] = true; result.push(arr[i]); } } return result; }

性能对比测试

javascript

// 生成测试数据 const testArray = []; for (let i = 0; i < 10000; i++) { testArray.push(Math.floor(Math.random() * 1000)); } // 性能测试函数 function testPerformance(fn, arr, name) { const start = performance.now(); fn([...arr]); // 避免修改原数组 const end = performance.now(); console.log(`${name}: ${(end - start).toFixed(2)}ms`); } // 测试 console.log('10000个元素的数组去重性能对比:'); testPerformance(uniqueSet, testArray, 'Set'); testPerformance(uniqueFilter, testArray, 'Filter'); testPerformance(uniqueReduce, testArray, 'Reduce'); testPerformance(uniqueDoubleLoop, testArray, '双层循环'); testPerformance(uniqueObject, testArray, 'Object键值');

性能排名(从快到慢)

  1. Set- O(n) - 最快,代码最简洁

  2. Object键值- O(n) - 较快,但仅适用于字符串或可转字符串的值

  3. Filter + indexOf- O(n²) - 中等,代码简洁但性能随数据量下降

  4. Reduce- O(n²) - 中等偏慢

  5. 双层循环- O(n²) - 最慢


特殊场景考虑

处理复杂对象

javascript

// 对象数组去重(基于某个属性) function uniqueByKey(arr, key) { const map = new Map(); return arr.filter(item => { if (!map.has(item[key])) { map.set(item[key], true); return true; } return false; }); } // 使用JSON.stringify(性能较差) function uniqueObjects(arr) { const set = new Set(); return arr.filter(obj => { const str = JSON.stringify(obj); if (set.has(str)) return false; set.add(str); return true; }); }

保持顺序

大多数方法(Set、Filter、Reduce)都能保持原始顺序,但Object键值不保证顺序。


总结建议

方法推荐度适用场景
Set⭐⭐⭐⭐⭐绝大多数场景,ES6环境
Object键值⭐⭐⭐简单值且不关心类型转换
Filter⭐⭐小数组,简洁代码优先
Reduce⭐⭐需要复杂处理逻辑
双层循环学习算法时,实际开发不推荐

最佳实践:

  • 现代项目直接使用[...new Set(arr)]

  • 需要考虑兼容性时,使用polyfill或lodash的_.uniq()

  • 大型数组优先考虑时间复杂度O(n)的方法


扩展运算符(Spread Operator)...总结

类别语法描述示例
基本概念...iterable将可迭代对象展开为独立元素...array,...string
数组操作
复制数组const newArr = [...arr]浅拷贝数组const arr1 = [1,2,3]; const arr2 = [...arr1]
合并数组[...arr1, ...arr2]合并多个数组[1,2, ...arr, 4,5]
数组去重[...new Set(arr)]配合Set快速去重[...new Set([1,2,2,3])] // [1,2,3]
插入元素[first, ...rest]结合解构分离首元素const [first, ...others] = [1,2,3,4]
对象操作
复制对象const newObj = {...obj}浅拷贝对象属性const obj1 = {a:1}; const obj2 = {...obj1}
合并对象{...obj1, ...obj2}合并对象,后者覆盖前者{...defaults, ...options}
添加属性{...obj, newProp: value}添加新属性{...user, age: 25}
覆盖属性{...obj, prop: newValue}修改已有属性值{...user, name: 'Bob'}
函数应用
函数参数func(...args)将数组展开为函数参数Math.max(...[1,2,3])
剩余参数function(...args){}收集剩余参数为数组function sum(...nums){...}
字符串操作
字符串转数组[...str]正确处理Unicode字符[...'hello'] // ['h','e','l','l','o']
表情符号处理[...'👨‍👩‍👧']正确处理组合emoji[...'👍🏽'] // ['👍🏽']
可迭代对象
NodeList转数组[...document.querySelectorAll('div')]类数组转真正数组const divs = [...document.querySelectorAll('div')]
Arguments转数组[...arguments]函数arguments转数组function f(){ return [...arguments]; }
Map/Set转数组[...map],[...set]Map/Set转为数组[...new Set([1,2,2])] // [1,2]
组合使用
数组+解构[first, ...rest] = arr提取首元素和剩余数组const [head, ...tail] = [1,2,3,4]
对象+解构{a, ...rest} = obj提取指定属性和剩余属性const {id, ...data} = obj
嵌套展开[...arr1, mid, ...arr2]在任意位置展开数组[0, ...middle, 9]
注意事项
浅拷贝{...obj},[...arr]只拷贝一层,嵌套对象引用共享{...obj}不会深拷贝嵌套对象
不可迭代错误...123数字等不可迭代对象会报错...123 // TypeError
性能考虑大数组展开大数组展开可能影响性能避免在循环中频繁展开大数组
特殊技巧
条件展开{...obj, ...(cond && {prop: val})}条件添加属性{...obj, ...(show && {visible: true})}
移除属性{propToRemove, ...rest} = obj结合解构移除属性const {password, ...safeUser} = user
默认值+覆盖{...defaults, ...overrides}设置默认值并允许覆盖const config = {...defaultConfig, ...userConfig}

关键特点总结

  1. 浅拷贝:只复制一层,嵌套对象是引用

  2. 可迭代性:只能用于可迭代对象(数组、字符串、Set、Map等)

  3. 不可变操作:创建新对象/数组,不修改原数据

  4. 顺序重要:在对象合并中,后面的属性覆盖前面的

  5. ES6+特性:需要Babel转译以支持旧浏览器


实用示例

javascript

// 1. 快速数组合并去重 const uniqueMerge = [...new Set([...arr1, ...arr2])]; // 2. 带默认值的对象配置 const createConfig = (userConfig) => ({ port: 3000, host: 'localhost', ...userConfig }); // 3. 安全的删除属性 const removePassword = ({password, ...user}) => user; // 4. 数组插入元素 const insertAt = (arr, index, ...items) => [ ...arr.slice(0, index), ...items, ...arr.slice(index) ];

扩展运算符是现代JavaScript开发中最常用、最简洁的特性之一,极大简化了数组和对象的操作

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/2 23:28:46

FastMCP之Resources

一、什么是资源 资源代表 MCP 客户端可以读取的数据或文件&#xff0c;而资源模板通过允许客户端基于 URI 中传递的参数请求动态生成的资源&#xff0c;扩展了这一概念。 FastMCP 简化了静态和动态资源的定义&#xff0c;主要使用 mcp.resource 装饰器。 当客户端请求资源统一资…

作者头像 李华
网站建设 2026/9/1 13:39:07

php反序列化学习

php反序列化学习在PHP中&#xff0c;反序列化漏洞&#xff08;也称为PHP对象注入漏洞&#xff09;是一个安全漏洞&#xff0c;它允许攻击者执行恶意代码或者访问敏感数据。这种漏洞通常发生在应用程序不正确地处理来自不可靠来源的序列化数据时就是将对象的状态信息写成一串字符…

作者头像 李华
网站建设 2026/9/1 14:25:13

为什么越来越多项目开始从 JDK 8 升级到 JDK 21?

从 JDK 8 升级到 JDK 21&#xff1a;哪些新特性真的好用&#xff1f;一篇写给 Java 工程师的实战指南先给结论&#xff1a; JDK 8 之后的升级&#xff0c;并不是语法糖的简单堆叠&#xff0c;而是一次并发模型、性能体系、代码表达能力的系统性进化。 JDK 21 作为最新 LTS&…

作者头像 李华
网站建设 2026/9/3 3:04:25

uniapp 实现路由切或者tab切换详细步骤

uniapp 实现路由切或者tab切换详细步骤 在 UniApp 中实现页面路由和标签页&#xff08;Tab&#xff09;切换是核心开发技能。下面为你梳理详细步骤和不同场景的实践方案。 下面表格中帮助你快速了解 UniApp 中主要的页面跳转方式及其区别&#xff1a;跳转方法作用适用场景关键限…

作者头像 李华