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);
此代码段完成了三个关键操作:
- 使用
arrayFilter过滤arrays,只保留类数组对象,过滤掉可能的非法参数 - 将过滤后的数组和比较器传给
baseXor函数 - 注意第二个参数是
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]与其他元素都不相等,所以保留在结果中
该实现允许处理复杂的对象比较场景,可以基于对象的深层结构或自定义规则进行异或操作。
总结
- 灵活的参数处理机制:通过检测最后一个参数类型,实现了优雅的函数重载,使 API 更直观
- 自定义比较策略:通过比较器函数分离了相等性判断逻辑,大幅提升了函数的适用范围
- 安全的参数验证:严格的类型检查和参数过滤确保了函数的健壮性和可靠性
- 函数组合的实践:展示了如何通过组合多个小函数构建复杂功能,体现了函数式编程的精髓