从简入繁:详解 JS 柯里化,实现 add(1, 2)(3)(4, ...)(5, ...)... 无限累加函数。
什么是柯里化
- 官方定义:把一个接收多个参数的函数,设计成一个:接收单一参数的函数,并返回一个能够接收剩余参数的新函数,最后返回结果。
- 简单理解:调用函数A运行后返回函数B,B处理A剩余参数并返回函数C,C处理B剩余参数并返回函数D,...,最后返回结果。
// 普通函数
const add = (a, b, c) => {
return a + b + c
}
// 柯里化函数
const curry_add = (a) => {
return (b) => {
return (c) => {
return a + b + c
}
}
}
改造和拆解
1. 简单的递归函数
const curry_add = (a) => {
return curry_add
}
curry_add(1)(2)(3)(4)...
简单实现无限调用,每次执行结果返回函数本身。
- 思考:返回递归函数的同时,如何将每次执行结果的值存储在函数中?
2. 像极了闭包和记忆函数
const curry_add = (a) => {
let num = a || 0
const item = (b) => {
num+= b
console.log(num, '循环执行')
return item
}
return item
}
curry_add(1)(2)(3)(4)...
# 3 "循环执行"
# 6 "循环执行"
# 10 "循环执行"
在
curry_add
函数被初次调用时,创建了一个拥有num 变量
和item 函数
的作用域函数,在第二次和后面的执行过程中,只是一直在累计 num
和调用 item
的重复工作,直到curry_add
函数执行结束并销毁num
和item
。
- 思考:如果将结果传递透出?
3. 不那么严谨的万物皆对象(简单实现)
const curry_add = (a) => {
let num = a || 0
const item = (b) => {
num+= b
item.num = num // 将 num 赋到函数上
return item
}
return item
}
curry_add(1)(2)(3)(4).num
# 10
除 8 种基本类型外,万物皆为对象。所以会在
item
函数每次执行时将num
变量赋到item.num
变量上,在curry_add
执行结束后用.num
调出结果。
- 思考:如何在执行结束后返回结果而中间步骤返回函数?
4. 共生、自执行函数与隐式转换
const curry_add = (a) => {
let num = a || 0
const item = (b) => {
num+= b
return item
}
item.toString = () => {
console.log('参数变化,自动触发')
return num
}
return item
}
curry_add(1)(2)(3)(4)
# ƒ 10
官方说明:
Object.prototype.toString()
方法返回一个表示该对象的字符串,当对象被表示为文本值时或者当以期望字符串的方式引用对象时,该方法被自动调用。
简单理解:在某个操作或者运算需要字符串而该对象又不是字符串的时候,会触发该对象的 String 转换,会将非字符串的类型尝试自动转为 String 类型。
- 思考:如何实现 curry_add(1)(2, 3)(4)... ?
5. 展开运算符 ...
const curry_add = (...a) => {
let num = a.reduce((t, c) => t + c, 0)
const item = (...b) => {
num = num + b.reduce((t, c) => t + c, 0)
return item
}
item.toString = () => num
return item
}
curry_add(1)(2, 3)(4)
# ƒ 10
初次执行用
reduce
计算结果,在递归函数中也加入reduce
累加结果,reduce
为函数基本操作,无内容补充。
结语
在日常开发中无时无刻不遇到隐式转换的问题,例如 ==
与 ===
的问题,先转换再比较,只是我们并没有去深入了解它。所以在 js 中各种奇奇怪怪的知识点,也够我们研究好些年了,哈~
都看这儿了,点个赞再走呗~
转载自:https://juejin.cn/post/6850418115042836487