Lodash源码阅读-xorWith

164 阅读4分钟

Lodash 源码阅读-xorWith

概述

xorWith 是 Lodash 中用于实现数组异或操作的函数,它允许通过自定义比较器函数来决定元素是否相等。该函数接收多个数组与一个比较器函数,返回仅在一个数组中出现的元素,使用比较器函数控制元素相等性判断,特别适合处理复杂对象的异或操作。

前置学习

依赖函数

  • baseXor:实现异或操作的核心函数,执行多个数组间的对称差集计算
  • baseRest:处理函数的剩余参数,实现类似 ES6 的 rest 参数功能
  • last:获取数组的最后一个元素,用于提取比较器函数
  • arrayFilter:过滤数组元素,根据条件筛选有效参数
  • isArrayLikeObject:检查值是否为类数组对象,确保参数有效性

技术知识

  • 高阶函数:函数作为参数和返回值的应用模式
  • 集合操作:集合论中的对称差(异或)概念及其实现原理
  • 比较器模式:自定义比较逻辑在复杂数据结构比较中的应用
  • 参数处理:可变参数的收集与有效性验证技术
  • 函数式编程:纯函数、函数组合等函数式编程概念

源码实现

var xorWith = baseRest(function (arrays) {
  var comparator = last(arrays);
  comparator = typeof comparator == "function" ? comparator : undefined;
  return baseXor(arrayFilter(arrays, isArrayLikeObject), undefined, comparator);
});

实现思路

xorWith 函数通过巧妙设计实现了带自定义比较器的数组异或操作。

  • 首先使用 baseRest 创建一个能处理不定数量参数的函数;
  • 然后尝试从参数的最后一个元素中提取比较器函数;
  • 接着过滤输入参数,只保留有效的类数组对象;
  • 最后调用 baseXor 计算异或结果,将比较器传入作为判断元素相等性的依据。

这种设计使得函数既能处理多个数组输入,又能灵活支持自定义的元素比较逻辑。

源码解析

1. 函数定义与参数收集

var xorWith = baseRest(function(arrays) {

xorWith 使用 baseRest 创建了一个可接收任意数量参数的函数。这种处理方式使得用户可以传入多个数组和一个比较器函数,所有参数都被收集到 arrays 数组中。

实际调用示例:

_.xorWith(
  [
    { x: 1, y: 2 },
    { x: 2, y: 1 },
  ],
  [
    { x: 1, y: 1 },
    { x: 1, y: 2 },
  ],
  _.isEqual
);
// arrays = [[{ x: 1, y: 2 }, { x: 2, y: 1 }], [{ x: 1, y: 1 }, { x: 1, y: 2 }], _.isEqual]

2. 比较器函数提取与验证

var comparator = last(arrays);
comparator = typeof comparator == "function" ? comparator : undefined;

这段代码从 arrays 中提取最后一个元素,并检查它是否为函数。如果是函数,则将其作为比较器;如果不是,则将比较器设为 undefined,这会导致后续使用默认的严格相等比较。

该步骤允许以下两种调用方式:

// 提供比较器函数
_.xorWith(array1, array2, customComparator);

// 不提供比较器函数,使用默认比较
_.xorWith(array1, array2);

值得注意的是,与 xorBy 不同,xorWith 严格要求比较器必须是函数类型,而不能是其他类型的值。这设计符合比较器的本质特性,它只能是函数而不能是其他形式。

3. 参数过滤与异或计算

return baseXor(arrayFilter(arrays, isArrayLikeObject), undefined, comparator);

此代码段完成了三个关键操作:

  1. 使用 arrayFilter 过滤 arrays,只保留类数组对象,过滤掉可能的非法参数
  2. 将过滤后的数组和比较器传给 baseXor 函数
  3. 注意第二个参数是 undefined,表示不使用迭代器(区别于 xorBy

当使用比较器时,调用流程如下:

var objects = [
  { x: 1, y: 2 },
  { x: 2, y: 1 },
];
var others = [
  { x: 1, y: 1 },
  { x: 1, y: 2 },
];

_.xorWith(objects, others, _.isEqual);
// 结果: [{ x: 2, y: 1 }, { x: 1, y: 1 }]

在这个例子中:

  • objects[0]{ x: 1, y: 2 })与 others[1]{ x: 1, y: 2 })通过 _.isEqual 比较,判定为相等,所以被排除
  • objects[1]others[0] 与其他元素都不相等,所以保留在结果中

该实现允许处理复杂的对象比较场景,可以基于对象的深层结构或自定义规则进行异或操作。

总结

  1. 灵活的参数处理机制:通过检测最后一个参数类型,实现了优雅的函数重载,使 API 更直观
  2. 自定义比较策略:通过比较器函数分离了相等性判断逻辑,大幅提升了函数的适用范围
  3. 安全的参数验证:严格的类型检查和参数过滤确保了函数的健壮性和可靠性
  4. 函数组合的实践:展示了如何通过组合多个小函数构建复杂功能,体现了函数式编程的精髓