Lodash 源码阅读-dropWhile
概述
dropWhile
函数创建一个新数组,从原数组开头删除满足条件的元素,直到遇到第一个不满足条件的元素为止。它是一个动态过滤函数,根据元素内容决定删除数量,而不是简单地删除固定数量。
前置学习
依赖函数
- baseWhile:内部工具函数,实现基于条件的数组切片核心逻辑
- getIteratee:获取迭代函数,支持多种简写形式
技术知识
- 高阶函数:函数作为参数的使用方式
- 谓词函数:返回布尔值的条件判断函数
- 迭代器简写:Lodash 支持的迭代器多种形式
- 数组切片:条件性数组切片操作
- 短路求值:条件满足时提前结束迭代
源码实现
javascript
/**
* Creates a slice of `array` excluding elements dropped from the beginning.
* Elements are dropped until `predicate` returns falsey. The predicate is
* invoked with three arguments: (value, index, array).
*
* @static
* @memberOf _
* @since 3.0.0
* @category Array
* @param {Array} array The array to query.
* @param {Function} [predicate=_.identity] The function invoked per iteration.
* @returns {Array} Returns the slice of `array`.
* @example
*
* var users = [
* { 'user': 'barney', 'active': false },
* { 'user': 'fred', 'active': false },
* { 'user': 'pebbles', 'active': true }
* ];
*
* _.dropWhile(users, function(o) { return !o.active; });
* // => objects for ['pebbles']
*
* // The `_.matches` iteratee shorthand.
* _.dropWhile(users, { 'user': 'barney', 'active': false });
* // => objects for ['fred', 'pebbles']
*
* // The `_.matchesProperty` iteratee shorthand.
* _.dropWhile(users, ['active', false]);
* // => objects for ['pebbles']
*
* // The `_.property` iteratee shorthand.
* _.dropWhile(users, 'active');
* // => objects for ['barney', 'fred', 'pebbles']
*/
function dropWhile(array, predicate) {
return array && array.length
? baseWhile(array, getIteratee(predicate, 3), true)
: [];
}
实现思路
dropWhile
函数的实现围绕两个关键点:输入验证和条件删除。
首先检查数组是否有效,如果无效则返回空数组。若有效,则调用 baseWhile
函数,传入数组、处理过的迭代函数和 true
参数(表示执行"删除"而非"保留"操作)。baseWhile
从数组开头开始迭代,持续删除满足条件的元素,直到遇到第一个不满足条件的元素,然后返回剩余部分。
源码解析
参数验证与空值处理
javascript
return array && array.length ? ... : [];
这行代码检查输入数组是否存在且非空:
- 如果数组为
null
、undefined
或空数组,直接返回空数组[]
- 使用短路逻辑
&&
简洁地实现多条件检查 - 这确保了函数对各种边缘情况的安全处理
迭代器转换
javascript
getIteratee(predicate, 3);
getIteratee
函数将各种类型的 predicate
转换为标准函数:
- 数字
3
表示迭代函数接收三个参数:值、索引、原数组 - 如果
predicate
已是函数,直接使用 - 如果是对象
{user: 'barney'}
,转为属性匹配函数 - 如果是数组
['active', false]
,转为属性值匹配函数 - 如果是字符串
'active'
,转为属性访问函数
这种转换让 dropWhile
支持多种简写形式,如:
javascript
// 函数形式
_.dropWhile(users, (user) => !user.active);
// 对象匹配形式
_.dropWhile(users, { user: "barney", active: false });
// 属性-值匹配形式
_.dropWhile(users, ["active", false]);
// 属性访问形式
_.dropWhile(users, "active");
baseWhile 调用
javascript
baseWhile(array, getIteratee(predicate, 3), true);
这是函数核心,调用 baseWhile
执行实际删除:
- 第一个参数是要处理的数组
- 第二个参数是转换后的谓词函数
- 第三个参数
true
指定这是一个"删除"操作
baseWhile
的工作流程:
- 从数组开头开始迭代
- 对每个元素应用谓词函数
- 持续迭代直到谓词返回
false
- 返回从第一个不满足条件的元素到数组末尾的切片
例如,对于数组 [2, 4, 6, 3, 8]
和条件 x => x % 2 === 0
(是否为偶数):
- 元素 2:满足条件,继续
- 元素 4:满足条件,继续
- 元素 6:满足条件,继续
- 元素 3:不满足条件,停止
- 返回
[3, 8]
(从第一个不满足条件的元素开始)
总结
dropWhile
函数体现了几个核心设计原则:
- 单一职责:专注于一个明确任务(条件删除),不包含多余功能
- 组合性:通过组合内部函数实现功能,增强代码复用
- 声明式编程:通过指定"条件"而非详细步骤,使代码更易理解
- 不可变性:不修改原数组,返回新数组,避免副作用
- 灵活性:支持多种谓词表达方式,适应不同使用场景
这个设计让我们在日常开发中能够用简洁代码表达复杂的条件过滤逻辑,提高代码可读性和可维护性。