Lodash源码阅读-baseXor

140 阅读4分钟

Lodash 源码阅读-baseXor

概述

baseXor 是 Lodash 内部的基础函数,用于实现数组的异或操作(XOR)。该函数接收多个数组作为输入,返回仅在其中一个数组中出现的元素集合,并支持自定义比较器和迭代器。它是 _.xor_.xorBy_.xorWith 等公共方法的核心实现。

前置学习

依赖函数

  • baseDifference:计算两个数组之间的差集,支持自定义比较器和迭代器
  • baseUniq:创建数组的去重版本,支持自定义比较函数
  • baseFlatten:将多维数组扁平化处理为指定深度的一维数组
  • arrayFilter:根据指定条件过滤数组元素

技术知识

  • 异或操作:集合的对称差概念,返回仅在一个集合中存在的元素
  • 数组操作:数组遍历技术与差集计算
  • 高阶函数:迭代器和比较器函数的应用
  • 函数式编程:组合多个基础函数实现复杂逻辑

源码实现

function baseXor(arrays, iteratee, comparator) {
  var length = arrays.length;
  if (length < 2) {
    return length ? baseUniq(arrays[0]) : [];
  }
  var index = -1,
    result = Array(length);

  while (++index < length) {
    var array = arrays[index],
      othIndex = -1;

    while (++othIndex < length) {
      if (othIndex != index) {
        result[index] = baseDifference(
          result[index] || array,
          arrays[othIndex],
          iteratee,
          comparator
        );
      }
    }
  }
  return baseUniq(baseFlatten(result, 1), iteratee, comparator);
}

实现思路

baseXor 实现多数组异或操作的核心思路是通过差集运算。首先判断输入数组数量,对每个数组与其他所有数组进行差集运算,获取只在当前数组中存在的元素。然后将所有结果扁平化并去重,得到最终的异或结果。这种实现方式能够处理任意数量的输入数组,并通过迭代器和比较器提供自定义比较逻辑。

源码解析

1. 参数处理与边界检查

function baseXor(arrays, iteratee, comparator) {
  var length = arrays.length;
  if (length < 2) {
    return length ? baseUniq(arrays[0]) : [];
  }

函数首先检查传入的数组数量。如果只有一个数组,则直接返回该数组的去重结果;如果没有数组,则返回空数组。这是对特殊情况的处理,确保函数行为的一致性。

2. 初始化结果数组

var index = -1,
  result = Array(length);

创建与输入数组数量相同长度的结果数组,用于存储中间计算结果。通过预分配数组大小,避免了动态扩容的性能开销。

3. 异或核心计算逻辑

while (++index < length) {
  var array = arrays[index],
    othIndex = -1;

  while (++othIndex < length) {
    if (othIndex != index) {
      result[index] = baseDifference(
        result[index] || array,
        arrays[othIndex],
        iteratee,
        comparator
      );
    }
  }
}

这个嵌套循环是整个异或操作的核心:

  • 外层循环遍历每个输入数组
  • 内层循环与其他所有数组计算差集
  • othIndex != index 确保不与自身比较
  • result[index] || array 在首次计算时使用原数组,后续使用累积的差集结果
  • baseDifference 支持通过 iterateecomparator 自定义比较方式

表达式result[index] || array的工作原理是这样的:

在计算过程中,我们需要把每个数组与其他所有数组进行差集计算。对于每个数组:

  1. 首次计算时result[index]还是undefined(因为刚开始结果数组是空的)

    • 此时undefined || array会返回原始数组array
    • 所以第一次计算会用原始数组作为基础
  2. 后续计算时result[index]已经存储了上一次计算的差集结果

    • 比如与第一个数组计算完差集后,结果存入result[index]
    • 再与第二个数组计算时,就用上一步的结果而不是原始数组了

举个例子,假设有三个数组[1,2], [2,3], [3,4]

  • 处理第一个数组时:
    • 与第二个数组计算差集:[1,2] - [2,3] = [1](去掉共有元素 2)
    • [1]存入result[0]
    • 与第三个数组计算差集:[1] - [3,4] = [1](没有共有元素)
    • 最终result[0][1]

这样通过多次迭代,每个数组都只保留了其独有的元素。

例如,对于输入 [[1,2], [2,3], [1,3]]

  • 第一个数组 [1,2] 经过处理后,只保留了 [](都被其他数组包含)
  • 第二个数组 [2,3] 经过处理后,只保留了 []
  • 第三个数组 [1,3] 经过处理后,只保留了 []

4. 结果处理与返回

  return baseUniq(baseFlatten(result, 1), iteratee, comparator);
}

最后将结果数组扁平化为一维数组,并使用相同的迭代器和比较器进行去重,得到最终的异或结果。baseFlatten 的参数 1 表示只扁平化一层,确保得到正确的一维结果数组。

总结

baseXor 函数通过巧妙的差集计算实现了数组的异或操作,其实现体现了以下几个关键技术点:

  1. 利用多次差集计算模拟异或操作,优化了多数组情况下的处理策略
  2. 通过参数预分配和条件判断优化算法性能
  3. 支持自定义比较逻辑,提高了函数的适用范围和灵活性
  4. 复用基础函数实现复杂逻辑,体现了良好的模块化设计思想

现代 JavaScript 中可使用 Set 实现类似功能,但 Lodash 的实现提供了更丰富的自定义能力和边界处理。