Lodash 源码阅读-baseIteratee
概述
baseIteratee
是 Lodash 内部的核心工具函数,它的任务很简单:把各种类型的值转换成标准的迭代器函数。这个函数是 Lodash 中 map
、filter
、find
等高阶函数的基础,让这些函数能接受多种形式的参数,大大提高了 API 的灵活性和易用性。
前置学习
依赖函数
isArray
:判断一个值是否为数组identity
:返回原值的函数,即value => value
baseMatches
:创建一个函数,检查对象是否匹配指定的源对象属性baseMatchesProperty
:创建一个函数,检查对象的指定路径上的值是否匹配给定值property
:创建一个函数,返回对象指定路径上的属性值
技术知识
- 高阶函数:接收或返回函数的函数
- 函数式编程:使用函数作为主要构建块的编程思想
- 类型检查:根据值的类型执行不同的逻辑
- 短路优化:根据条件提前返回结果,避免不必要的计算
- 策略模式:根据输入类型选择不同的处理策略
源码实现
javascript
function baseIteratee(value) {
// Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9.
// See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details.
if (typeof value == "function") {
return value;
}
if (value == null) {
return identity;
}
if (typeof value == "object") {
return isArray(value)
? baseMatchesProperty(value[0], value[1])
: baseMatches(value);
}
return property(value);
}
实现思路
baseIteratee
的核心思想是根据输入值的类型返回合适的迭代器函数:
- 如果输入是函数,直接返回这个函数,不做任何处理
- 如果输入是
null
或undefined
,返回identity
函数(原样返回值) - 如果输入是对象类型:
- 若是数组(如
['name', 'fred']
),返回检查对象属性值的函数 - 若是普通对象,返回检查对象属性匹配的函数
- 若是数组(如
- 如果是其他类型(字符串、数字等),返回获取对象属性的函数
这种设计让 Lodash 的高阶函数非常灵活,可以接受各种形式的参数,使 API 更加简洁易用。
源码解析
函数类型处理
javascript
if (typeof value == "function") {
return value;
}
首先检查 value
是否为函数。如果是函数,直接返回它。
javascript
// 使用函数作为迭代器
_.map([1, 2, 3], function (n) {
return n * 2;
});
// => [2, 4, 6]
注释中提到了 Safari 9 的 JIT 编译器 bug,这就是为什么不把 typeof value
结果存在变量里,而是直接在条件判断中使用。
空值处理
javascript
if (value == null) {
return identity;
}
如果 value
是 null
或 undefined
(==
可以同时检查这两种情况),返回 identity
函数:
javascript
function identity(value) {
return value;
}
这样处理后,当传入 null
时,迭代器会返回集合中的每个元素本身:
javascript
// 使用 null 作为迭代器
_.map([1, 2, 3], null);
// => [1, 2, 3]
对象类型处理
javascript
if (typeof value == "object") {
return isArray(value)
? baseMatchesProperty(value[0], value[1])
: baseMatches(value);
}
如果 value
是对象类型,还要进一步区分:
- 如果是数组(用
isArray
判断),假设它的格式是[path, srcValue]
,表示"检查对象在指定路径上的值是否等于 srcValue":
javascript
// 查找 'user' 属性值为 'barney' 的用户
_.find(users, ["user", "barney"]);
// 等价于 _.find(users, function(o) { return o.user === 'barney'; });
- 如果是普通对象,返回
baseMatches(value)
创建的函数,用于检查对象是否包含与该对象匹配的属性:
javascript
// 查找匹配指定属性的对象
_.find(users, { user: "barney", active: true });
// 等价于 _.find(users, function(o) { return o.user === 'barney' && o.active === true; });
其他类型处理
javascript
return property(value);
如果 value
不是上述任何类型,就返回 property(value)
创建的函数,用于获取对象的属性值:
javascript
// 提取所有用户的 'user' 属性
_.map(users, "user");
// 等价于 _.map(users, function(o) { return o.user; });
property
函数根据路径类型选择合适的实现:
javascript
function property(path) {
return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path);
}
总结
baseIteratee
是 Lodash 中一个巧妙设计的核心函数,它通过简单的类型检查,实现了强大的迭代器转换功能。它的特点包括:
- 接口统一:不管输入什么类型,总是返回一个函数,让高阶函数可以统一处理
- 灵活多变:支持函数、属性路径、对象匹配等多种方式创建迭代器
- 简单易用:将复杂的类型判断和转换逻辑封装起来,对外提供简洁的接口
- 可扩展性 :通过
_.iteratee
方法,允许用户定制迭代器的行为
这个函数虽然简短,但它大大增强了 Lodash API 的灵活性和易用性,是整个库的重要基石。通过学习它的设计,我们可以了解如何设计灵活而强大的函数式 API。