JavaScript数组去重的四种实现方案与原理分析

作者:纸飞机时间:2026-10-22 08:30:20浏览:438

本文详细解析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简化、逆向标记优化以及哈希表映射。每种方法在性能、兼容性和处理复杂数据类型(如对象、数组)的能力上各有优劣。开发者应根据具体的业务场景、性能要求及目标浏览器环境选择最合适的方法。如有任何疑问或需要进一步探讨,欢迎留言交流。感谢支持!

作者最新文章