--愿你内心有种不灭的火焰,将你与别人区分开来--

0%

函数柯理化及其应用

函数柯理化及其应用

高级阶数

高阶函数 (一个函数的参数是函数,或是将函数作为返回值的函数) 在 Javascript 中随处可见(比如数组中的 forEach(),sort(),map()……),比起普通函数,高阶函数要灵活太多。除了传统意义上的函数调用返回,还形成了一种后续传递的风格(Continuation Passing Style) 的结果接收方式。

函数柯里化

Currying 是函数式编程的一个特性,我们可以将多个参数的处理转化成单个参数的处理,类似链式调用。同时柯里化也是一个逐步传参,逐步缩小函数的适用范围,逐步求解的过程。

柯里化有3个常见作用:1. 参数复用;2. 提前返回;3. 延迟计算/运行。

普通的函数求解过程是这样的:

1
2
3
function commFn(a,b,c){
return a + b + c;
}

而函数柯理化是分部求解,先传一个a参数,再传一个b参数,再传一个c参数,最后将这三个参数相加:

1
2
3
4
5
6
7
function fn(a){
return function(b){
return function(c){
return a+b+c
}
}
}

参考:

下面是柯理化的基础版本。

柯理化基础版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function currying (fn){
var args = Array.prototype.slice.call(arguments, 1);
// 获得最外层函数的参数,fn 不算在内
return function(){
var innerArgs = Array.prototype.slice.call(arguments)
// 获得内层函数的参数
var finalArgs = args.concat(innerArgs);
// 组装外层函数和内层函数的参数
return fn.apply(null, finalArgs);
};
};

function add() {
let result = 0
Array.from(arguments).map(item => result+=item)
return result;
}

let curredAdd = currying(add,4,5,6)(1,2,3);//21

那么,进一步,如果我们想传任意多个参数,而当不传参数时输出结果呢?下面是柯理化的延迟计算版本

柯理化任意多参数延迟计算版本

1
2
3
4
5
6
7
8
9
10
11
function currying(fn) {
let args= [].slice.call(arguments,1)
return function A () {
let innerArgs = [].slice.call(arguments)
args.push(...innerArgs)
if (innerArgs.length === 0){
return fn.apply(null,args)
}
return A
}
}

假如,我们有一个 add 函数,并对其进行柯理化:

1
2
3
4
5
6
7
8
9
10
function add() {
let result = 0
Array.from(arguments).map(item => result+=item)
return result;
}

let curredAdd = currying(add);
curredAdd(100,200)(300);
curredAdd(400)
console.log(curredAdd())

curredAdd 是柯里化了的函数,它返回一个新的函数,新的函数接收可分批次接受新的参数,延迟到最后一次计算。我们可以任意传入参数,当不传参数的时候,输出计算结果!

类似函数柯里化等 高阶函数 (一个函数的参数是函数,或是将函数作为返回值的函数),在 Node 的异步编程中十分常见;而且,函数柯里化也是 ES6 的函数尾调用优化的技术基础。

函数绑定 bind 方法

除此以外,函数柯里化还常作为函数绑定的一部分包含在其中,以构造出更为复杂的绑定函数:

1
2
3
4
5
6
function bind(fn, context){
var args = Array.property.slice.call(arguments, 2);
return function (){
return fn.apply(context, args);
}
}

其实 ECMAScript5 已经函数定义了一个原生的 bind() 方法,我们可以直接使用它。

JavaScript 中的函数柯里化和函数绑定提供了强大的动态函数创建功能,它们都能用于创建复杂的算法和功能,但是两者都不应滥用,因为每个函数都会带来额外的开销。