美文网首页
【js基础】函数柯理化的实现

【js基础】函数柯理化的实现

作者: SophieRabbit | 来源:发表于2020-08-28 16:42 被阅读0次

    函数式编程

    与面向对象编程(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函数。

    参考链接:

    http://www.ruanyifeng.com/blog/2017/02/fp-tutorial.html

    https://www.jianshu.com/p/5e1899fe7d6b

    相关文章

      网友评论

          本文标题:【js基础】函数柯理化的实现

          本文链接:https://www.haomeiwen.com/subject/qdnxsktx.html