1、一些术语
1.1 一元函数
只接受一个参数的函数称为一元函数
const identity = (x) => x复制代码
1.2 二元函数
接受两个参数的函数
const add = (x,y) => x + y复制代码
1.3 变参函数
接受可变数量参数的函数
// es5 function variadic(a){ console.log(a) console.log(arguments) } // es6 const variadic = (a, ...variadic) => { console.log(a) console.log(variadic) }复制代码
2、什么是柯里化?
柯里化就是把一个多参数函数转换为一个嵌套的一元函数的过程。
呵呵,定义看似简单易懂。但还是让人感觉镜中花,水中月。摸不透柯里化的本质。不要急,看官们。让我们先从简单的二元函数add
柯里化开始。
2.1 二元函数柯里化
const addCurried = x => y => x + y// 其实就是闭包function addCurried (x){ return function (y){ return x + y }}addCurried(4)(3) // 7复制代码
上面是手动地把接受二元函数add
转换为含有嵌套的一元的函数。下面展示如何把该处理过程转换为一个名为curry
的方法
// es5const curry = (fn){ return function (firstParam){ return function (secondParam){ return fn(firstParam, secondParam) } }}// es6let curry = fn => firstParam => secondParam => fn(firstParam, secondParam)复制代码
至此,对于函数柯里化,我们应该揭开了TA神秘的面纱。 但是大多数我们写的函数都是变参的。直接看下面变参函数柯里化
2.2 变参函数柯里化
2.2.1 简单变参函数柯里化
let curry = (fn) => { if(typeof fn !== 'function'){ throw Error ('参数必须是函数') } return function curriedFn(...args){ return fn.apply(null,args) }}复制代码
如果我们有个multiply
函数
// es 6const multiply = (x,y,z)=> x*y*z复制代码
我们可以通过以下方式使用2.2中的curry
函数
curry(multiply)(1,2,3) // 6复制代码
下面我们看看,curry
是如何运行的。 我们在curry
函数里添加了如下代码:
return function curriedFn(...args){ return fn.apply(null,args)}复制代码
返回函数是一个变参函数,它返回了传入args
并通过apply
调用函数的结果
fn.apply(null, args)复制代码
通过curry(multiply)(1,2,3)
,args
将会指向[1,2,3]
,由于我们调用了fn
的apply
,等价于:
multiply(1,2,3)复制代码
通过2.2 部分我们实现了变参函数的柯里化,接下来我们实现把多参函数转换为一元函数的curry
函数,就如函数柯里化的定义一样。
2.2.2 转换为嵌套的一元函数的curry
函数
let curry = (fn)=>{ if(typeof fn !== 'function'){ throw Error('参数必须是函数') } return function curriedFn(...args){ if(args.length < fn.length){ return function(){ return curriedFn.apply(null,args.concat([].slice.call(arguments))) } } return fn.apply(null args) }}复制代码
和之前的curry
函数相比我们添加了
if(args.length < fn.length){ return function(){ return curriedFn.apply(null,args.concat([].slice.call(arguments))) }}复制代码
让我们逐句理解在这段代码中发生了什么
args.length < fn.length复制代码
这行代码是检查通过...args
传入的参数长度是否小于函数fn
参数列表的长度。如果是,就进入if
代码块,如果不是,就如之前一样调用整个函数fn
。 一旦进入if
代码块,就使用apply
函数递归地调用curriedFn
函数:
curriedFn.apply(null,args.concat([].slice.call(arguments)))复制代码
此代码片段中:
args.concat([].slice.call(arguments))复制代码
非常重要,我们使用concat
函数连接每次传入的参数,并递归地调用curriedFn
。由于我们将所有传入的参数组合并递归调用,终将
if(args.length < fn.length)复制代码
条件失败,即参数列表长度args
和函数参数的长度fn.length
相等。程序将调用整个函数fn
return fn.apply(null args)复制代码
这将产生函数的完整的结果。
好了,函数柯里化就说到这了。 噗。。。感觉写的不好,大佬们请多多关照