大家好,我是江城开朗的豌豆,一名拥有6年以上前端开发经验的工程师。我精通HTML、CSS、JavaScript
等基础前端技术,并深入掌握Vue、React、Uniapp、Flutter
等主流框架,能够高效解决各类前端开发问题。在我的技术栈中,除了常见的前端开发技术,我还擅长3D开发,熟练使用Three.js
进行3D图形绘制,并在虚拟现实与数字孪生技术上积累了丰富的经验,特别是在虚幻引擎开发方面,有着深入的理解和实践。
我一直认为技术的不断探索和实践是进步的源泉,近年来,我深入研究大数据算法的应用与发展,尤其在数据可视化和交互体验方面,取得了显著的成果。我也注重与团队的合作,能够有效地推动项目的进展和优化开发流程。现在,我担任全栈工程师,拥有CSDN博客专家认证及阿里云专家博主称号,希望通过分享我的技术心得与经验,帮助更多人提升自己的技术水平,成为更优秀的开发者。
技术qq交流群:906392632
大家好,我是小杨,一个沉迷于JavaScript各种骚操作的前端老司机。今天咱们来玩点有意思的------如何实现一个可以无限链式调用的add函数,最终在空调用时返回累加结果?听起来是不是很酷?让我们一步步揭开这个技巧的神秘面纱!
一、先看看我们要实现的效果
javascript
add(1)(2)(3)(); // 期望输出 6
add(1,2,3)(4)(); // 期望输出 10
add(1)(2,3)(4,5)(); // 期望输出 15
这种函数调用方式在函数式编程中被称为柯里化(Currying) ,但比普通柯里化更灵活,因为它支持:
- 单参数或多参数调用
- 无限链式调用
- 空调用时返回计算结果
二、基础版:单参数链式调用
我们先从简单的单参数版本来理解核心思路:
javascript
function add(num) {
let sum = num;
const innerAdd = (nextNum) => {
if (nextNum === undefined) {
return sum;
}
sum += nextNum;
return innerAdd;
};
return innerAdd;
}
console.log(add(1)(2)(3)()); // 输出 6
关键点解析:
-
add
函数初始化累加值 -
返回的
innerAdd
函数可以:- 接收新数字并累加,然后返回自身(继续链式调用)
- 无参数调用时返回累加结果
-
通过闭包保持对
sum
的引用
三、升级版:支持多参数调用
现在我们来增强功能,支持每次调用传入多个参数:
javascript
function add(...args) {
let sum = args.reduce((acc, val) => acc + val, 0);
const innerAdd = (...nextArgs) => {
if (nextArgs.length === 0) {
return sum;
}
sum += nextArgs.reduce((acc, val) => acc + val, 0);
return innerAdd;
};
return innerAdd;
}
console.log(add(1,2,3)(4)()); // 输出 10
console.log(add(1)(2,3)(4,5)()); // 输出 15
改进点:
- 使用剩余参数
...args
接收任意数量参数 - 用
reduce
计算参数总和 - 同样通过闭包保持
sum
的状态
四、终极版:支持直接取值和链式调用
有时候我们可能想直接获取当前值而不需要空调用:
javascript
function add(...args) {
let sum = args.reduce((acc, val) => acc + val, 0);
const innerAdd = (...nextArgs) => {
sum += nextArgs.reduce((acc, val) => acc + val, 0);
return innerAdd;
};
// 添加valueOf方法,可以在需要原始值时自动调用
innerAdd.valueOf = () => sum;
// 添加toString方法,方便输出查看
innerAdd.toString = () => sum.toString();
return innerAdd;
}
// 使用方式1:传统空调用
console.log(add(1)(2)(3)()); // 输出 6
// 使用方式2:直接参与运算(自动调用valueOf)
const result = add(1)(2)(3) + 4; // 6 + 4 = 10
console.log(result); // 输出 10
// 使用方式3:直接输出(自动调用toString)
console.log(add(1)(2)(3)); // 输出 6
高级技巧:
- 实现
valueOf
方法让对象在需要原始值时自动转换 - 实现
toString
方法让对象在被当作字符串时友好显示 - 这样函数既可以被链式调用,也可以直接参与运算
五、原理深度剖析
这个实现的魔法主要依赖于几个JavaScript特性:
- 闭包(Closure) :内部函数保持对外部变量的引用
- 高阶函数(Higher-order Function) :函数返回函数
- 剩余参数(Rest Parameters) :处理不定数量参数
- 对象原始值转换 :通过
valueOf
和toString
控制对象到原始值的转换
六、实际应用场景
虽然这种写法看起来很炫酷,但在实际项目中要谨慎使用。适合的场景包括:
- 构建数学计算库的流畅接口
- 创建DSL(领域特定语言)
- 函数式编程工具函数
- 面试时展示JS功底(笑)
七、扩展思考:如何实现减法?
基于同样思路,我们可以扩展出支持加减乘除的链式计算器:
javascript
function calc(initial = 0) {
let result = initial;
const methods = {
add: (...args) => {
result += args.reduce((a, b) => a + b, 0);
return methods;
},
subtract: (...args) => {
result -= args.reduce((a, b) => a + b, 0);
return methods;
},
valueOf: () => result
};
return methods;
}
const total = calc()
.add(1,2).subtract(3)
.add(4).add(5,6) + 7;
console.log(total); // 输出 16
八、总结与最佳实践
-
核心模式:函数返回函数 + 闭包保存状态
-
参数处理:使用剩余参数处理多参数情况
-
终止条件:空调用或隐式转换触发结果返回
-
注意事项:
- 这种模式可能降低代码可读性
- 在团队项目中要确保大家都理解这种写法
- 考虑使用TypeScript添加类型提示
记住,强大的JavaScript特性是一把双刃剑,用得好能让代码更优雅,用不好会让同事抓狂。关键是找到平衡点!