【js基础】函数柯理化的实现
函数式编程
与面向对象编程(Object-oriented programming)和过程式编程(Procedural programming)并列的编程范式。
最主要的特征是,函数是第一等公民。
强调将计算过程分解成可复用的函数,典型例子就是map方法和reduce方法组合而成 MapReduce 算法。
只有纯的、没有副作用的函数,才是合格的函数。
为什么函数式编程要求函数必须是纯的,不能有副作用?因为它是一种数学运算,原始目的就是求值,不做其他事情,否则就无法满足函数运算法则了。
总之,在函数式编程中,函数就是一个管道(pipe)。这头进去一个值,那头就会出来一个新的值,没有其他作用。
函数式编程有两个最基本的运算:合成和柯里化。
合成
如果一个值要经过多个函数,才能变成另外一个值,就可以把所有中间步骤合并成一个函数,这叫做"函数的合成"(compose)。
const compose = function (f,g) {
return function(x) {
return f(g(x));
};
}
有一个函数,接收函数F作为参数,运行后能够返回一个新的函数。并且这个新的函数能够处理函数F的剩余参数。
f(x)和g(x)合成为f(g(x)),有一个隐藏的前提,就是f和g都只能接受一个参数。如果可以接受多个参数,比如f(x, y)和g(a, b, c),函数合成就非常麻烦。
这时就需要函数柯里化了。所谓"柯里化",就是把一个多参数的函数,转化为单参数函数,并且返回接受余下的参数而且返回结果的新函数的技术。
function add(a,b,c) {
return a+b+c;
}
经过柯理化后的结果:每次只需要传入一个参数了
function _add(a) {
return function(b) {
return function(c) {
return a + b + c;
}
}
}
以上代码可以使得add(1,2,3)与_add(1)(2)(3)变得等价。但在传参方面,变为可以分多步传参。
柯理化的通用范式
当然,靠眼力封装的柯里化函数自由度偏低,柯里化通用式具备更加强大的能力。因此我们需要知道如何去封装这样一个柯里化的通用式。
// 简单实现,参数只能从右到左传递
function createCurry(func, args) { // func需要处理的函数, args是func函数的部分参数
var funLen = func.length; // 原始函数的参数的长度
var args = args || []; // 后续的参数们
return function() { // 这里的arguments
// 改arguments为数组( [].slice.call() 常用来将类数组转化为真正的数组 )
var _args = [].slice.call(arguments); // 这里的_args为: []
[].push.apply(_args, args); // 等价于_args.push(...args), [].push.apply是为了防止报错
// [].push.apply: https://blog.csdn.net/qq_29055201/article/details/84972285
// 如果参数个数小于最初的func.length,则递归调用,继续收集参数
if (_args.length < funLen) { // 当前参数个数不全
return createCurry.call(this, func, _args);
// xxx.call(this)使用func对象代替this对象,返回一个柯理化函数等待下次参数补全
}
// 参数收集完毕,则执行func
return func.apply(this, _args);
}
}
柯理化的案例:
function check(targetString, reg) { // 这是一个返回正则的函数
return reg.test(targetString);
}
var _check = createCurry(check); // 返回一个柯理化后的函数,可以后续传参生成函数;
var checkPhone = _check(/^1[34578]\d{9}$/);
var checkEmail = _check(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/);
checkPhone('183888888'); // 很好用
checkEmail('xxxxx@test.com');
继续来思考一个例子。这个例子与map有关。在高阶函数的章节中,我们分析了封装map方法的思考过程。由于我们没有办法确认一个数组在遍历时会执行什么操作,因此我们只能将调用for循环的这个统一逻辑封装起来,而具体的操作则通过参数传入的形式让使用者自定义。这就是map函数。
参考链接: