Lodash 源码阅读-baseRest
概述
baseRest
是 Lodash 内部工具函数,用于模拟 ES6 的剩余参数功能。它通过组合 overRest
和 setToString
两个函数,创建一个能处理不定数量参数的新函数,并保留原函数的字符串表示。这在实现柯里化和函数部分应用中非常有用。
前置学习
依赖函数
- overRest:处理剩余参数的核心函数
- setToString:保持函数的 toString 方法,便于调试
- identity :返回输入值本身的函数,即
x => x
源码实现
js
function baseRest(func, start) {
return setToString(overRest(func, start, identity), func + "");
}
实现思路
baseRest
函数虽然只有一行代码,但实现了两个关键功能:
- 使用
overRest
创建一个能处理剩余参数的新函数 - 使用
setToString
保留原函数的字符串表示
为什么这样做?因为在 JavaScript 中,函数的 toString()
方法会返回函数的源代码,这对调试和函数识别很有用。当使用高阶函数时,新函数默认的 toString()
不会反映原函数的信息,所以需要专门设置。
源码解析
函数签名:function baseRest(func, start)
func
: 需要处理的原始函数start
: 从第几个参数开始算作"剩余参数"(索引从 0 开始)
实现解析:
js
return setToString(overRest(func, start, identity), func + "");
- 使用
overRest
创建处理剩余参数的新函数 - 用
identity
保持剩余参数原样不变 - 用
setToString
保留原函数的字符串表示
参数处理核心原理
当使用 baseRest
创建的函数被调用时,参数处理过程如下:
js
// 创建一个包装函数
const wrapper = baseRest(function (first, rest) {
console.log(first); // 10
console.log(rest); // [20, 30, 40]
return first + rest.reduce((sum, n) => sum + n, 0);
}, 1);
// 调用包装函数
wrapper(10, 20, 30, 40); // 结果: 100
处理流程:
-
参数分割:
- 固定参数:
[10]
(索引 0 之前的参数) - 剩余参数:
[20, 30, 40]
(索引 1 及之后的参数)
- 固定参数:
-
调用准备:
- 对剩余参数应用
identity
函数(保持不变) - 形成调用数组
otherArgs = [10, [20, 30, 40]]
- 对剩余参数应用
-
函数调用:
- 通过
apply(func, this, otherArgs)
调用 - 相当于
func(10, [20, 30, 40])
- 形参
first
接收第一个参数10
- 形参
rest
接收第二个参数[20, 30, 40]
- 通过
应用场景示例
1. 求和函数
js
const sum = baseRest(function (first, rest) {
return rest.reduce((total, num) => total + num, first);
}, 1);
sum(10, 20, 30, 40); // => 100
2. 数据库查询构建器
js
const query = baseRest(function (table, conditions) {
return `SELECT * FROM ${table} WHERE ${conditions.join(" AND ")}`;
}, 1);
query("users", "age > 18", "status = 'active'");
// => "SELECT * FROM users WHERE age > 18 AND status = 'active'"
3. 事件处理器
js
const createEventHandler = baseRest(function (eventName, listeners) {
return function (data) {
console.log(
`事件「${eventName}」触发,共有 ${listeners.length} 个处理函数`
);
listeners.forEach((listener) => listener(data));
};
}, 1);
const clickHandler = createEventHandler(
"点击",
(data) => console.log("处理器1:", data),
(data) => console.log("处理器2:", data)
);
clickHandler({ x: 100, y: 200 });
总结
baseRest
主要解决两个问题:
- 参数处理:在 ES6 之前环境中模拟剩余参数功能
- 元数据保留:保持原函数的字符串表示
即使在现代 JavaScript 已有原生剩余参数的情况下,baseRest
在实现函数式编程模式、处理兼容性和创建高级函数工具时仍然非常有价值。