You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Partial application (or partial function application) refers to the process of fixing a number of arguments to a function, producing another function of smaller arity.
Javascript 中的柯里化(Currying)
什么是柯里化
柯里化(Currying)是一种函数式编程1的技巧,Wikipedia2是这么描述它的:
简单来说,柯里化是一种将一个接收 N 个参数的函数转变为 N 个只接收一个参数的函数的技术,也就是:
举个例子
假设今天甲方要让你计算一批固定长宽的长方体体积:
这么写显然有些麻烦了,不只是要复制多次,而且如果之后甲方要修改长或者宽的成本会很大,我们可以用柯里化改写如下:
这时候有些同学可能就会问了,也没什么不同呀🤔。别急,我们可以根据上面再改写一下:
通过这种方式,我们实现了参数复用。
柯里化 vs Partial Application
细心的同学可能发现了我在第三段代码写上了 “Partial Application” 作为与柯里化的区分,由于国内的帖子很少专门讨论这两个概念的异同,所以我决定在这里花一点篇幅介绍一下。
Partial Application 在 Wikipedia3 上是这么介绍的:
简单来说,它是一个将部分参数事先 “绑定” 到函数上,然后返回需要更少参数的函数的方法。Function.prototype.bind()4 方法在原生上支持了 Partial Application。
两者的区别在于:柯里化负责将有多个参数的函数转变为多个接收一个参数的函数;Partial application 则是负责 “绑定” 一些参数到函数上并返回绑定后的函数。
柯里化的基础
相信用过柯里化的同学都会听过一句话:柯里化是闭包的概念的一种应用。
所以下面我将会用一点篇幅来介绍一下闭包的概念:
什么是闭包
根据 Wikipedia5,闭包的定义是:
简单来说,闭包是一种函数词法范围绑定的技术。
举个例子
可以看出来,因为 foo 函数返回了 bar,导致本来应该被回收的作用域没有被回收,而且仍然可以被 baz 函数使用,这就是闭包的作用。
回到柯里化,让我们看看刚刚的例子:
我们来解析一下这段函数到底做了什么事:
calculateVolume
接收了一个参数length
并且返回了一个函数getWidth
,这个时候就产生了一个闭包(getWidth
取得了length
的访问权)getHeight
也产生了一个闭包(getHeight
取得了length
与width
的访问权)getHeight
内部,通过闭包取得length
与width
的值,再加上自己的参数height
,把这三者相乘之后返回柯里化的实现
参数定长函数的柯里化
参数不定长函数的柯里化
其实上面的 currying 函数已经可以满足大部分的应用场景了,但是考虑到如下函数:
上述的函数可以提供运行时的函数参数计算,为了支持这种函数,我们的 currying 需要那么亿点点的修改,主要有三种思路。
加一个参数
上面修改后的版本看似完美的完成了任务,但是它对用户实在是不怎么友好,有两个严重问题:
undefined
)dynamicAdd
)传了一个undefined
,可能会导致程序运行不符合预期(以dynamicAdd
为例,总是返回 0)约定一个范式
上面的版本最大的区别在于第 6 行,判断传入的参数是否为空,如果是则执行运算,否则继续等待更多参数传入。
与上述添加参数的版本相比,这个方法无疑会好的多,但是还是要有一定的沟通成本。
魔改原型
这个思路源自于 Tsui 大佬的博客6,他魔改了函数原型的 toString 方法,这将使得返回的函数在进行运算的时候会根据抽象的 ToPrimitive 操作隐式调用 toString 方法,从而能被当成数字处理。Respect
总结
柯里化的有点可以总结如下:
Reference
https://zh.javascript.info/currying-partials
https://ithelp.ithome.com.tw/articles/10195145
https://segmentfault.com/a/1190000021677898
https://juejin.cn/post/6889250555035090951
Footnotes
https://en.wikipedia.org/wiki/Functional_programming ↩
https://en.wikipedia.org/wiki/Currying ↩
https://en.wikipedia.org/wiki/Partial_application ↩
https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Function/bind ↩
https://en.wikipedia.org/wiki/Closure_(computer_programming) ↩
https://juejin.cn/post/6864378349512065038 ↩
The text was updated successfully, but these errors were encountered: