JavaScript数组去重的四种实现方案与原理分析
本文详细解析JavaScript中数组去重的四种常见实现方式。从基础的双层循环到利用indexOf语法糖,再到基于Object哈希表的键值映射,以及针对特定场景的性能优化技巧。文章对比了各方法在处理基本类型、引用类型及低版本浏览器兼容性上的差异,并提供完整代码示例,帮助开发者根据实际需求选择最优方案。
数组去重核心逻辑与元素判定标准
数组去重的核心需求通常是将给定数组作为输入,调用去重函数后返回一个不含重复项的新数组副本。在判断元素是否重复时,默认采用严格相等运算符(===)进行比较。这意味着,数值1与字符串"1"被视为不同元素,原始数值1与包装对象new Number(1)也被视为不同元素。此外,两个独立创建的空对象{}即使内容相同,由于引用地址不同,在默认去重逻辑中也被视为不同元素(若业务需求将两个空对象视为相同,则需采用其他去重策略)。
方法一:基础双层循环实现
方法一:基础双层循环实现
该方法通过两层循环遍历数组。外层循环选取当前元素,内层循环在结果数组中查找是否存在该元素。若内层循环结束未找到匹配项,则将当前元素加入结果数组。
function unique(arr) {
var res = [];
for(var i = 0, len = arr.length;i < len; i++) {
var item = arr[i];
for(var j = 0, jLen = res.length; j<jLen; j++) {
if(item == res[j]) break;
}
if(j == jLen) res.push(item);
}
return res;
}
方法二:利用indexOf简化逻辑
方法二:利用indexOf简化逻辑
function unique(arr) {
var ret = []
for (var i = 0; i < arr.length; i++) {
var item = arr[i]
if (ret.indexOf(item) === -1) {
ret.push(item)
}
}
return ret
}
在方法一的基础上,可以使用数组的indexOf方法作为语法糖来简化判断逻辑。indexOf返回元素在数组中的索引,若返回-1则表示该元素尚未存在于结果数组中。
function unique(arr) {
var res = [];
for(var i = 0, len = arr.length;i < len; i++) {
var item = arr[i];
(res.indexOf(item) === -1) && res.push(item);
}
return res;
}
需要注意的是,indexOf方法在低版本浏览器(如IE8及以下)中并不支持。因此,在实际项目中若需兼容旧环境,可能需要手动实现indexOf逻辑或采用Polyfill。
var indexOf = [].indexOf ?
function(arr, item) {
return arr.indexOf(item)
} :
function indexOf(arr, item) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] === item) {
return i
}
}
return -1
}
function unique(arr) {
var ret = []
for (var i = 0; i < arr.length; i++) {
var item = arr[i]
if (indexOf(ret, item) === -1) {
ret.push(item)
}
}
return ret
}
方法三:逆向比较与索引优化技巧
方法三:逆向比较与标记优化
这是双层循环的另一种变体。与前一种方法逐一比较原数组元素与结果数组不同,此方法通过比较原数组中当前元素与其后续元素是否相同。若发现重复,则标记该元素为重复项,仅将未被标记的元素(即重复元素的最后一个实例或唯一元素)加入结果数组。
function unique(arr) {
var ret = [];
var len = arr.length;
var isRepeat;
for(var i=0; i<len; i++) {
isRepeat = false;
for(var j=i+1; j<len; j++) {
if(arr[i] === arr[j]){
isRepeat = true;
break;
}
}
if(!isRepeat){
ret.push(arr[i]);
}
}
return ret;
}
在此逻辑基础上,可以进一步优化循环效率。通过巧妙调整内层循环的索引变量,当发现重复元素时,直接跳过后续相同的元素,从而减少不必要的比较次数。
function unique(a) {
var res = [];
for (var i = 0, len = a.length; i < len; i++) {
for (var j = i + 1; j < len; j++) {
// 这一步十分巧妙
// 如果发现相同元素
// 则 i 自增进入下一个循环比较
if (a[i] === a[j])
j = ++i; //j = i = i + 1;
}
res.push(a[i]);
}
return res;
}
方法四:基于Object哈希表的实现与类型处理
方法四:基于Object哈希表的实现
利用JavaScript中Object对象的键(key)唯一性特性,可以将数组元素作为键存储在对象中,从而实现去重。这种方法通常结合filter或reduce使用,通过检查哈希表中是否已存在该键来判断是否重复。
function dedup(arr) {
var hashTable = {};
return arr.filter(function(value,index,arr){
var key = JSON.stringify(value);
var match = Boolean(hashTable[key]);
return (match ? false : hashTable[key] = true);
});
}
由于Object的键默认会被转换为字符串类型,这导致数值1和字符串"1"在哈希表中会被视为相同的键。为了解决类型混淆问题,可以在构建键时同时包含元素的数据类型和值,例如使用typeof(item) + item作为键,从而区分不同类型的相同值。
function dedup(arr) {
var ret = [];
var hash = {};
for(var i = 0; i < arr.length; i++) {
var item = arr[i];
var key = typeof(item) + item;
if(hash[key] !== 1) {
ret.push(item)
hash[key] = 1;
}
}
return ret;
}
总结与方案选择建议
总结
以上介绍了JavaScript中数组去重的四种主要实现思路,包括基础循环、indexOf简化、逆向标记优化以及哈希表映射。每种方法在性能、兼容性和处理复杂数据类型(如对象、数组)的能力上各有优劣。开发者应根据具体的业务场景、性能要求及目标浏览器环境选择最合适的方法。如有任何疑问或需要进一步探讨,欢迎留言交流。感谢支持!
React中怎么不可变地删除JS数组元素
2026-11-05 18:06:01
JS动态构建复杂嵌套JSON对象的方法
2026-11-05 15:41:52
JS负数取余为什么是负数?详解余数与模运算区别
2026-11-05 13:34:41
JS 解压出现乱码或报错怎么解决?
2026-11-05 12:32:38
JS 中 startsWith、indexOf 与正则判断字符串前缀的对比
2026-11-04 22:19:43
