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
:支持自定义比较器进行比较