Lodash 源码阅读-baseWhile
概述
baseWhile
是 Lodash 内部的一个工具函数,它是实现 _.takeWhile
、_.dropWhile
、_.takeRightWhile
和 _.dropRightWhile
等方法的基础。这个函数负责根据指定条件从数组中提取或排除元素,支持从数组开头或结尾开始处理。
前置学习
依赖函数
baseSlice
:数组切片的底层实现函数getIteratee
:获取迭代器函数的工具
技术知识
- 数组操作:理解数组索引和切片操作
- 谓词函数:接受值并返回布尔值的函数
- 循环控制:使用条件控制循环的执行方式
- 高阶函数:接受或返回函数的函数设计模式
- 函数组合:将多个函数组合在一起形成新功能
源码实现
javascript
function baseWhile(array, predicate, isDrop, fromRight) {
var length = array.length,
index = fromRight ? length : -1;
while (
(fromRight ? index-- : ++index < length) &&
predicate(array[index], index, array)
) {}
return isDrop
? baseSlice(array, fromRight ? 0 : index, fromRight ? index + 1 : length)
: baseSlice(array, fromRight ? index + 1 : 0, fromRight ? length : index);
}
实现思路
baseWhile
函数的实现思路巧妙而高效,它通过四个核心步骤完成工作:
- 初始化必要的变量,包括数组长度和起始索引位置
- 根据
fromRight
参数决定从数组的开头还是末尾开始循环 - 使用
while
循环,对数组元素应用谓词函数,直到找到第一个不满足条件的元素 - 根据
isDrop
和fromRight
参数,选择适当的数组切片方式返回结果
这种实现允许一个函数同时支持四种不同的操作模式(take、drop、从左、从右),大大减少了代码重复。
源码解析
参数初始化
javascript
var length = array.length,
index = fromRight ? length : -1;
这里初始化了两个关键变量:
length
:数组的长度,用于控制循环边界index
:迭代的起始位置,根据fromRight
决定是从数组末尾(length
)开始,还是从开头前一个位置(-1
)开始
循环处理
javascript
while (
(fromRight ? index-- : ++index < length) &&
predicate(array[index], index, array)
) {}
这个循环包含两个关键部分:
-
索引更新与边界检查:
(fromRight ? index-- : ++index < length)
- 从右侧开始:递减
index
(index--
) - 从左侧开始:递增
index
并检查是否小于数组长度(++index < length
)
- 从右侧开始:递减
-
条件检查:
predicate(array[index], index, array)
- 对当前元素应用谓词函数
- 传入三个参数:当前元素值、索引和原数组
- 只要谓词返回真值,循环就会继续
这个循环会一直运行,直到遇到第一个不满足谓词条件的元素,或者遍历完整个数组。
结果处理
javascript
return isDrop
? baseSlice(array, fromRight ? 0 : index, fromRight ? index + 1 : length)
: baseSlice(array, fromRight ? index + 1 : 0, fromRight ? length : index);
这部分代码根据 isDrop
和 fromRight
参数,调用 baseSlice
从数组中返回适当的切片:
-
当
isDrop
为true
时(丢弃模式):- 从右侧开始:保留索引 0 到
index
的元素 - 从左侧开始:保留索引
index
到末尾的元素
- 从右侧开始:保留索引 0 到
-
当
isDrop
为false
时(保留模式):- 从右侧开始:保留索引
index+1
到末尾的元素 - 从左侧开始:保留索引 0 到
index
的元素
- 从右侧开始:保留索引
这种设计让 baseWhile
能够灵活地处理四种不同的操作模式。
isDrop 参数不同取值的运行过程
我们通过具体例子来说明 isDrop
参数如何影响函数行为:
假设有数组 [1, 2, 3, 4, 5]
,谓词函数为 x => x < 3
(即检查元素是否小于 3):
1. isDrop = false(保留满足条件的元素)
这对应于 takeWhile
和 takeRightWhile
函数:
-
从左开始 (fromRight = false):
- 循环从索引 0 开始,依次检查元素[1, 2],直到遇到元素 3(不满足条件)
- 此时
index = 2
- 返回
baseSlice(array, 0, 2)
→[1, 2]
- 这就是
takeWhile
的行为:保留开头连续满足条件的元素
-
从右开始 (fromRight = true):
- 循环从末尾(索引 4)开始,因为没有元素满足条件(都大于等于 3)
- 此时
index = 4
(循环未进入) - 返回
baseSlice(array, 5, 5)
→[]
- 如果数组是
[6, 5, 4, 2, 1]
,则会返回[2, 1]
- 这就是
takeRightWhile
的行为:保留末尾连续满足条件的元素
2. isDrop = true(丢弃满足条件的元素)
这对应于 dropWhile
和 dropRightWhile
函数:
-
从左开始 (fromRight = false):
- 循环从索引 0 开始,依次检查元素[1, 2],直到遇到元素 3(不满足条件)
- 此时
index = 2
- 返回
baseSlice(array, 2, 5)
→[3, 4, 5]
- 这就是
dropWhile
的行为:丢弃开头连续满足条件的元素
-
从右开始 (fromRight = true):
- 循环从末尾(索引 4)开始,因为没有元素满足条件(都大于等于 3)
- 此时
index = 4
(循环未进入) - 返回
baseSlice(array, 0, 5)
→[1, 2, 3, 4, 5]
(原数组) - 如果数组是
[6, 5, 4, 2, 1]
,则会返回[6, 5, 4]
- 这就是
dropRightWhile
的行为:丢弃末尾连续满足条件的元素
这种设计通过组合 isDrop
和 fromRight
参数,使得一个函数可以实现四种相关但不同的操作,展示了 Lodash 优秀的代码复用能力。注意循环的终止条件和切片范围的选择是这个函数的精髓所在。
总结
baseWhile
函数展示了 Lodash 优秀的代码设计原则:
- 代码复用:一个函数通过参数组合支持多种功能,减少重复代码
- 抽象分层:将核心逻辑抽象到内部函数,公共 API 只需简单包装
- 参数灵活性:通过布尔参数控制函数行为,实现多态
- 高效实现:通过精心设计的循环和条件判断,确保性能最优