Lodash 源码阅读-unionWith
概述
unionWith 函数是 Lodash 中用于合并数组的高级工具,它接受多个数组参数和一个比较器函数,用于自定义元素间的相等性判断。该函数返回一个新数组,包含所有数组的唯一值,其中重复性由比较器函数决定。结果数组中元素的顺序取决于它们在原数组中首次出现的位置。
前置学习
依赖函数
- baseRest:模拟 ES6 的剩余参数功能,用于处理不定数量的函数参数
- baseFlatten:将嵌套数组结构扁平化到指定深度
- baseUniq:数组去重的基础实现,支持迭代器和比较器
- isArrayLikeObject:检查值是否为类数组对象
- last:获取数组的最后一个元素
技术知识
- 函数式编程中的高阶函数和参数柯里化
- 自定义比较逻辑实现
- 数组扁平化和去重算法
- 不定参数处理机制
- JavaScript 对象比较的局限性与解决方案
源码实现
var unionWith = baseRest(function (arrays) {
var comparator = last(arrays);
comparator = typeof comparator == "function" ? comparator : undefined;
return baseUniq(
baseFlatten(arrays, 1, isArrayLikeObject, true),
undefined,
comparator
);
});
实现思路
unionWith 函数通过巧妙组合多个基础函数实现其功能。
- 首先使用
baseRest处理不定数量的数组参数; - 然后检查最后一个参数是否为函数类型,若是则将其作为比较器,否则设为 undefined;
- 接着使用
baseFlatten将所有输入数组扁平化为一维数组; - 最后调用
baseUniq根据提供的比较器函数对扁平化数组进行去重,返回最终结果。 这种实现支持自定义相等性判断逻辑,使函数在处理复杂对象数组时更为灵活。
源码解析
1. 函数定义与参数处理
var unionWith = baseRest(function (arrays) {
// 函数体...
});
baseRest 在这里创建了一个新函数,将所有传入 unionWith 的参数收集到单个 arrays 数组中。这种设计允许函数接受任意数量的数组参数,实现了类似 ES6 中 ...args 的功能。
示例:当调用 unionWith(arr1, arr2, arr3, comparator) 时,arrays 将包含 [arr1, arr2, arr3, comparator]。
2. 提取并验证比较器函数
var comparator = last(arrays);
comparator = typeof comparator == "function" ? comparator : undefined;
这段代码获取并验证比较器函数:
- 使用
last函数获取arrays的最后一个元素 - 判断该元素是否为函数类型
- 若是函数,则将其用作比较器
- 若不是函数,则将比较器设为 undefined
这种处理确保了只有当最后一个参数确实是函数时,才会将其作为比较器使用,提高了函数的健壮性。
3. 数组扁平化与去重处理
return baseUniq(
baseFlatten(arrays, 1, isArrayLikeObject, true),
undefined,
comparator
);
这段代码完成了主要工作:
-
baseFlatten(arrays, 1, isArrayLikeObject, true)- 将数组扁平化:- 深度参数为 1,只处理一层嵌套
- 使用
isArrayLikeObject确保只扁平化类数组对象 - 最后一个参数
true表示移除最后一个元素(比较器函数)
-
baseUniq对扁平化后的数组进行去重:- 第一个参数是扁平化后的数组
- 第二个参数
undefined表示不使用迭代器 - 第三个参数是提取出的比较器函数
实际调用示例:
// 使用 _.isEqual 作为比较器
var objects = [
{ x: 1, y: 2 },
{ x: 2, y: 1 },
];
var others = [
{ x: 1, y: 1 },
{ x: 1, y: 2 },
];
_.unionWith(objects, others, _.isEqual);
// => [{ x: 1, y: 2 }, { x: 2, y: 1 }, { x: 1, y: 1 }]
在这个例子中,{ x: 1, y: 2 } 出现在两个数组中,通过 _.isEqual 比较被识别为相同对象,因此在结果中只出现一次。
比较器函数接收两个参数:当前处理的元素和结果数组中的某个元素。当比较器返回 true 时,表示两个元素相等,当前元素将被跳过。
总结
- 通过自定义比较函数实现复杂数据结构的相等性判断,解决了 JavaScript 原生全等比较的局限
- 使用函数式组合方式构建复杂功能,提高代码复用性和可维护性
- 优雅处理不定参数,通过参数位置和类型检测实现 API 的灵活性
- 结合扁平化和去重操作,在保持语义清晰的同时实现高效的数组处理