Lodash 源码阅读-without
概述
without 函数用于创建一个新数组,其中排除了所有给定的值。它使用 SameValueZero 算法进行相等性比较,这意味着它可以正确处理 NaN 等特殊值。与 _.pull 不同,without 不会修改原数组,而是返回一个新数组。
前置学习
依赖函数
- baseRest:用于处理不定参数,类似 ES6 的剩余参数功能
- isArrayLikeObject:检查值是否为类数组对象
- baseDifference:计算数组差集的基础实现
技术知识
- SameValueZero :ECMAScript 规范中的比较算法,与
===类似但能正确处理NaN - 不定参数处理:处理可变数量的函数参数
- 数组差集:从一个数组中移除另一个数组中存在的元素
源码实现
js
var without = baseRest(function (array, values) {
return isArrayLikeObject(array) ? baseDifference(array, values) : [];
});
实现思路
without 函数的实现非常简洁,主要分为三个步骤:
- 使用
baseRest包装函数,处理不定参数,将第一个参数识别为array,其余参数收集到values数组中 - 检查
array是否为类数组对象,确保输入有效 - 如果输入有效,调用
baseDifference计算差集;否则返回空数组
这种实现方式既简洁又高效,可以处理各种输入情况,同时保持了函数的健壮性。
源码解析
函数签名和参数处理
js
var without = baseRest(function(array, values) {
without 使用 baseRest 高阶函数进行包装,这使它能够接收不定数量的参数:
- 第一个参数
array是要处理的数组 - 剩余的所有参数会被收集到
values数组中
baseRest 的作用类似于 ES6 的剩余参数 ...values,但它提供了更多的灵活性和兼容性。
类型检查和结果计算
js
return isArrayLikeObject(array) ? baseDifference(array, values) : [];
这部分代码执行两个关键操作:
-
类型检查 :使用
isArrayLikeObject判断array是否为类数组对象- 类数组对象包括数组、arguments 对象、DOM 集合等
- 这个检查确保了函数能安全处理各种输入
-
差集计算:
- 如果
array是类数组对象,调用baseDifference(array, values)计算差集 - 如果
array不是类数组对象,直接返回空数组[],避免程序异常
- 如果
baseDifference 的工作原理
baseDifference 是 Lodash 内部的一个函数,用于计算数组差集。它的工作流程如下:
- 初始化结果数组
result = [] - 如果源数组为空,直接返回空结果
- 如果有
iteratee或comparator,使用相应的方式处理比较逻辑 - 对于大型数组,使用
SetCache优化比较性能 - 遍历源数组中的每个元素:
- 如果元素不在排除值列表中,将其添加到结果数组
- 使用严格相等比较(SameValueZero)或自定义比较器进行比较
使用示例
基本使用
js
_.without([2, 1, 2, 3], 1, 2);
// => [3]
在这个例子中:
- 源数组是
[2, 1, 2, 3] - 要排除的值是
1和2 - 结果是
[3],即源数组中所有非1和2的元素
对象数组比较
js
var object1 = { a: 1 };
var object2 = { b: 2 };
var array = [object1, object2];
_.without(array, { a: 1 });
// => [object1, object2],因为使用严格相等比较
_.without(array, object1);
// => [object2],成功移除了 object1
这个例子展示了 without 使用严格相等比较的特性:
- 即使
{ 'a': 1 }和object1内容相同,但它们是不同的对象引用 - 只有直接引用
object1才能成功排除它
总结
without 函数提供了一种简便的方式来创建排除特定值的新数组。其主要特点包括:
- 不可变性:不修改原数组,而是返回新数组,符合函数式编程原则
- 灵活的参数:支持排除多个值,提高了使用便利性
- 严格比较:使用 SameValueZero 算法进行比较,确保准确性
- 类型安全:对输入进行类型检查,增强了函数的健壮性
虽然 without 的功能可以通过 filter 实现,但它提供了更简洁的 API,特别适合从数组中快速排除一些已知值的场景。
如果需要更复杂的排除逻辑,可以考虑使用 Lodash 的其他方法:
_.difference:排除另一个数组中的所有值_.differenceBy:支持通过迭代器进行值转换后比较_.differenceWith:支持自定义比较器进行比较