导读:本期聚焦于小伙伴创作的《JavaScript数组去重有哪些方法?6种常用实现方案详解与性能对比》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《JavaScript数组去重有哪些方法?6种常用实现方案详解与性能对比》有用,将其分享出去将是对创作者最好的鼓励。

JavaScript数组去重实现方法详解

数组去重是JavaScript开发中非常常见的需求,比如处理用户多次提交的重复数据、过滤接口返回的冗余结果等场景都会用到。本文将介绍几种常用的数组去重方法,分析它们的实现逻辑和适用场景,帮助大家根据实际需求选择合适的方案。

一、使用Set结构去重

ES6引入的Set数据结构天生具有去重特性,它只存储唯一值,重复的元素会被自动过滤,这是目前最简洁的去重方式。

// 使用Set实现数组去重
function uniqueBySet(arr) {
    // 将数组转为Set,自动去重,再转回数组
    return Array.from(new Set(arr));
}

// 测试示例
const testArr1 = [1, 2, 2, 3, 3, 3, 'a', 'a', true, true];
console.log(uniqueBySet(testArr1)); // 输出:[1, 2, 3, 'a', true]

这种方法的优点是代码量少、逻辑清晰,Set使用的是SameValueZero算法比较值,能正确处理NaN的去重(因为Set认为NaN等于NaN,而常规全等比较认为NaN不等于NaN),同时可以处理字符串、布尔值等基本类型的去重。不过它无法处理对象类型的去重,因为对象的引用地址不同,即使内容相同也会被认为是不同的值。

二、使用双重循环+索引判断去重

这是ES6之前比较常用的传统去重方式,通过两层循环遍历数组,判断当前元素是否已经存在于新数组中。

// 双重循环实现数组去重
function uniqueByLoop(arr) {
    const result = [];
    for (let i = 0; i < arr.length; i++) {
        // 判断当前元素是否已经在结果数组中
        let isExist = false;
        for (let j = 0; j < result.length; j++) {
            if (arr[i] === result[j]) {
                isExist = true;
                break;
            }
        }
        // 不存在则加入结果数组
        if (!isExist) {
            result.push(arr[i]);
        }
    }
    return result;
}

// 测试示例
const testArr2 = [1, 2, 2, 3, 3, 3, 'a', 'a', NaN, NaN];
console.log(uniqueByLoop(testArr2)); // 输出:[1, 2, 3, 'a', NaN, NaN]

这种方法的兼容性很好,支持所有浏览器环境,但缺点也很明显:时间复杂度是O(n²),数组长度较大时性能较差;同时因为使用全等比较,无法处理NaN的去重,上述示例中两个NaN都会被保留。

三、使用indexOf方法去重

利用数组的indexOf方法,判断当前元素在结果数组中的索引,如果不存在(返回-1)就加入结果数组,相比双重循环代码更简洁。

// 使用indexOf实现数组去重
function uniqueByIndexOf(arr) {
    const result = [];
    for (let i = 0; i < arr.length; i++) {
        // indexOf返回当前元素在结果数组中的索引,不存在则返回-1
        if (result.indexOf(arr[i]) === -1) {
            result.push(arr[i]);
        }
    }
    return result;
}

// 测试示例
const testArr3 = [1, 2, 2, 3, '3', 3, 'a', 'a'];
console.log(uniqueByIndexOf(testArr3)); // 输出:[1, 2, 3, '3', 'a']

这种方法的逻辑比双重循环更清晰,同样存在无法处理NaN的问题,因为indexOf也无法找到NaN的索引,所以如果数组中有NaN,也会被重复保留。另外indexOf使用的是严格相等比较,不会做类型转换,所以数字3和字符串'3'会被认为是不同的值。

四、使用includes方法去重

ES6新增的includes方法可以直接判断数组是否包含某个元素,返回布尔值,比indexOf的判断更直观。

// 使用includes实现数组去重
function uniqueByIncludes(arr) {
    const result = [];
    for (let i = 0; i < arr.length; i++) {
        // includes判断结果数组是否包含当前元素
        if (!result.includes(arr[i])) {
            result.push(arr[i]);
        }
    }
    return result;
}

// 测试示例
const testArr4 = [1, 2, 2, 3, NaN, NaN, 'b', 'b'];
console.log(uniqueByIncludes(testArr4)); // 输出:[1, 2, 3, NaN, 'b']

includes方法可以正确判断NaN是否存在,所以这种方法能处理NaN的去重,这是它比indexOf更优的地方。不过它的时间复杂度依然是O(n²),性能和indexOf方法接近。

五、使用对象哈希去重

利用对象的键名唯一特性,将数组元素作为对象的键,遍历数组时判断键是否存在,实现O(n)时间复杂度的去重。

// 使用对象哈希实现数组去重
function uniqueByHash(arr) {
    const hash = {};
    const result = [];
    for (let i = 0; i < arr.length; i++) {
        // 将元素转为字符串作为键,避免对象键名自动转字符串的问题
        const key = typeof arr[i] + JSON.stringify(arr[i]);
        if (!hash[key]) {
            hash[key] = true;
            result.push(arr[i]);
        }
    }
    return result;
}

// 测试示例
const testArr5 = [1, '1', 2, 2, {name: 'test'}, {name: 'test'}, true, true];
console.log(uniqueByHash(testArr5)); // 输出:[1, '1', 2, {name: 'test'}, true]

这种方法的性能比循环类方法更好,时间复杂度是O(n),适合处理大数组。不过需要注意键名的处理:对象的键名会自动转为字符串,所以数字1和字符串'1'如果不做区分会被认为是同一个键,示例中通过拼接类型的方式来避免这个问题。另外如果数组中有对象,相同内容的对象依然会被认为是不同的,因为JSON.stringify不同引用地址的对象结果虽然相同,但示例中用typeof+JSON.stringify的方式可以把内容相同的对象去重,不过要注意如果对象中有函数、undefined等特殊值,JSON.stringify会处理成null或者忽略,需要根据实际场景调整。

六、方法对比与选择建议

去重方法时间复杂度是否支持NaN去重兼容性适用场景
Set结构去重O(n)ES6及以上环境现代浏览器环境,处理基本类型数组去重,追求代码简洁
双重循环去重O(n²)所有环境需要兼容低版本浏览器,数组长度较小的场景
indexOf去重O(n²)所有环境兼容低版本浏览器,不需要处理NaN的场景
includes去重O(n²)ES6及以上环境现代环境,需要处理NaN,不需要极致性能的场景
对象哈希去重O(n)所有环境大数组去重,需要高性能的场景,注意特殊值的处理

实际开发中,如果不需要兼容低版本浏览器,优先选择Set结构去重,代码简洁且性能不错;如果需要兼容IE等老环境,数组长度较小可以用indexOf或者双重循环,数组长度较大建议使用对象哈希的方式。如果数组中包含对象类型,需要根据对象的去重规则调整对应的方法,比如按对象某个属性去重,可以在现有方法基础上增加属性判断逻辑。

JavaScript数组去重Set去重indexOfincludes哈希表去重 本作品最后修改时间:2026-05-22 14:01:26

免责声明:网站部分内容来源于网络或由用户自行发表,内容观点不代表本站立场。本站是个人网站免费分享,内容仅供个人学习、研究或参考使用,如内容中引用了第三方作品,其版权归原作者所有。若内容触犯了您的权益,请联系我们进行处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。前端、网络、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握网站开发与运维所需的核心技术栈。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端逻辑,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。