javascript柯里化及组合函数~

大家是不是看我上篇博文有点蒙。用的的curry和compose是什么鬼,怎么那么神奇。上篇博文也是主要用到了这两个函数。
那今天我们来聊一下curry和compose,这两个东西是函数式编程很重要的东西,基本每个稍微复杂点的例子都要涉及这两个函数。
什么是curry呢?
---函数柯里化。就是这个东西了。举一个简单的例子。

var _console=curry(function(x){
    console.log(x);
})
_console("hello"); //hello

其实就这个作用,先定义函数,后传参数。_console保存了这个函数,每次调用这个函数的时候传给它参数。是不是很简单呢?
看一下curry的实现。

function curry(fn){
    return function(f){
        return fn(f);
    }
}

只是返回一个函数那么简单,这里还涉及fn的控制权反转,类似redux的dispatch,传入dispatch使用它。
那么当这个curry函数有2个参数的时候,curry就得变为:

function curry(fn){
    return function(f){
        return function(g){
            return fn(f,g);
        }
    }
}

那么有3个参数呢?

function curry(fn){
    return function(f){
        return function(g){
            return function(h){
                return fn(f,g,h);
            }
        }
    }
}

那么当我想一次传2个参数,剩下一个传1个参数怎么办?

function curry(fn){
    return function(f){
        return function(g,h){
            return fn(f,g,h);
        }
    }
}

那么我改主意了,我不确定每次传多少个参数了,我不确定一共传多少个参数了。怎么办?

自动柯里化函数,你会发现_.curry完美的解决了这个问题,甚至可以在你传一个空参数的时候忽略它。
今天我们不研究_.curry的实现,有大神可以分享一下~
我们今天写一个自动柯里化函数。
怎么写?什么思路呢?它的难点在哪?
首先我们需要知道自动柯里化函数的执行过程和普通柯里化函数的执行过程是一样的,即有多少参数返回多少的function,取到函数的参数的长我们知道,

但是函数又不是数组,可以用一层for循环return出来,只能改变思路,函数的循环就是递归了。
它的难点在哪?

我们需要在每次执行一次()的时候获取他的参数,在递归里我们可以声明一个外部变量而储存每次执行的参数。

但是这个是不行的,因为每次递归都需要返回一个function,这意味着你无法返回上一层了。那我们只能把执行的参数当函数的参数传进去了。
直接把自动柯里化给你们(复制可测试)

function curry (fn, length, args) {
    length = length || fn.length; //保存函数的参数数量
    args = args || [];
    return function(){
            var _args = args.slice(0),
                arg, i;
            for(i = 0; i < arguments.length; i++) {//遍历_args,存储每次执行的参数
                arg = arguments[i];
                _args.push(arg);
            }
            if(_args.length < length) {
                return curry.call(this, fn, length, _args); //递归调用自己
            } else {
                return fn.apply(this, _args);
            }
     }
}

可以看到解决return函数个数的是

if(_args.length < length) {
    return curry.call(this, fn, length, _args);
}else {
    return fn.apply(this, _args);
}

_arg储存了从开始到现在的所有的参数,如果参数”够了“(一个小知识,函数的length==函数参数的个数),就调用fn,把所有参数都传给他,

如果不够,就继续递归,递归的时候呢,把当前的参数传到下一层函数里。
难点就在于每次递归回来都要遍历一下当前的参数,把它push到_args里面。
这样做也避免了空参数的情况,当传入一个空参数,比如这样调用()。_args不变。

大家有空可以研究一下_.curry的实现过程是不是类似呢?-0-

下一个主角,compose,这个函数在fp里面叫组合函数,它能把里面的参数都组合起来,一起调用。类似流式

function compose(fn1,fn2){
    return function(arg){
     fn2(fn1(arg));
    }
}
var gen= compose(function(b){console.log(b);return b+"be deal with b";} , function(a){console.log(a);return a+"be deal with a";} )
gen("holle"); //hello hello deal with b;

gen就像一个水管,gen("holle");就是往水管里注入了水。
它会从compose左边的函数开始执行,hello是数据流,流过2个函数。第一个函数的返回值会传入到第二个参数继续执行。
在函数式编程里,compose是避免命令式的重要一环,fp要做到的每一个函数都是”纯“的,脏的东西交给使用者。把每个纯的函数组合到compose里面,就像gulp的pipe()一样,每个函数就像一个插件,来处理数据流。
那么我想从右到左处理数据流,

function compose(fn1,fn2){
    return function(arg){
     fn1(fn2(arg));
    }
}

那么和curry一样,我不想只能传2个函数,我想传任意函数怎么办?

这里我们不写自动组合函数了,因为在我分析redux源码的时候发现了这个函数,redux帮我们写了~我们就只来分析一下就好了。
先上一下源码。redux不跟lodash一样一个函数跳来跳去,lodash想只看一部分是不好懂的。redux的compose函数(复制可测试)。

function compose() {
      for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) {
        funcs[_key] = arguments[_key];    //把参数都复制到funcs数组里。
      }

      return function () {
        if (funcs.length === 0) {
          return arguments.length <= 0 ? undefined : arguments[0]; //处理无参数的情况,返回undefined
        } 

        var last = funcs[funcs.length - 1];    //这是最后一个处理函数
        var rest = funcs.slice(0, -1);       //取除了最后一个剩下的处理函数

        return rest.reduceRight(function (composed, f) {//每次都执行当前处理函数传入基数
          return f(composed);
        }, last.apply(undefined, arguments));    //执行最后一个处理函数,返回值作为reduce的基数。
      };
 } 

他的原理很简单,就是把arguments的所有函数都执行一次,把上个函数的返回值传入。这里巧妙的用了数组的reduceRight函数。
你换成reduce也可以,那这个last要换成first,这样就是从左到右执行啦。处理顺序的问题。

它俩是很有用的,例子可以看上篇博文~~点我
学好这两个函数,我们再打开函数式编程的大门吧~

时间: 2024-10-04 21:32:00

javascript柯里化及组合函数~的相关文章

【 js 基础 】【 源码学习 】柯里化和箭头函数

最近在看 redux 的源码,代码结构很简单,主要就是6个文件,其中 index.js 负责将剩余5个文件中定义的方法 export 出来,其他5个文件各自负责一个方法的实现. 大部分代码比较简单,很容易看懂,但是在 applyMiddleware.js 中 有一个地方还是很有意思,用到了柯里化和箭头函数的组合.由于增强 store,丰富 dispath 方法的时候,可能会用到多个 中间件,所以这个的嵌套有可能会很深,导致对 箭头函数和柯里化 不是很熟悉的童鞋,一看源码就会有些理不清思路. 一.

JavaScript柯里化

什么是柯里化 柯里化允许我们把函数与传递给它的参数结合,产生一个新的函数.[引自<JavaScript语言精髓>,第43页 柯里化] A function is said to be curried when not all arguments have been supplied to the function, so it returns another function that retains the arguments given and expects the remaining

currying 柯里化,返回函数

var currying = function(fn){var arg = [].slice.call(arguments,1);//获得除了fn之外的参数.return function(){//返回一个函数var newArgs = arg.concat([].slice.call(arguments));//把旧参数和新参数放在一起 .fn.apply(null,newArgs);//使用所有参数}}; var sayHi = function(){var args = [].slice.

201602021344_《Javascript柯里化uncurrying()(将内置方法独立成为一个通用方法)》

Function.prototype.uncurrying = function() { var that = this; return function() { return Function.prototype.call.apply(that, arguments); } }; function add(a,b){ return a + b; }; var myAdd = add.uncurrying(); console.log(add(5,6));// 11

JavaScript的柯里化函数

柯里化,或者说部分应用,是一种函数式编程的技术,对于熟悉以传统方式编写 JavaScript 代码的人来说可能会很费解.但如果使用得当,它可以使你的 JavaScript 函数更具可读性. 更具可读性和灵活性 函数式 JavaScript 被吹捧的优点之一就是拥有短小紧凑的代码风格,可以用最少行数.更少重复的代码得到正确的结果.有时这会以牺牲可读性为代价:如果你还不熟悉函数式编程的方法,这种方法写的代码会很难阅读和理解. 如果之前你遇到过柯里化这个术语,但是不知道它是什么意思,把它当做奇怪的.难

浅析 JavaScript 中的 函数 currying 柯里化

原文:浅析 JavaScript 中的 函数 currying 柯里化 何为Curry化/柯里化? curry化来源与数学家 Haskell Curry的名字 (编程语言 Haskell也是以他的名字命名). 柯里化通常也称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果.因此柯里化的过程是逐步传参,逐步缩小函数的适用范围,逐步求解的过程. 柯里化一个求和函数 按照分步求值,我们看一个

柯里化函数之Javascript

柯里化函数之Javascript 定义 根据定义来说,柯里化就是将一个接收"多个"参数的函数拆分成一个或者许多个接收"单一"参数的函数.定义看起来是比较抽象的,下面来举个例子: 代码 1 2 3 4 5 function concat(str1,str2){ return str1 + str2; } concat("forever","px") // "foreverpx" 不难理解,上面的代码中定义了一

理解运用JS的闭包、高阶函数、柯里化

一.闭包 1. 闭包的概念 闭包与执行上下文.环境.作用域息息相关 执行上下文 执行上下文是用于跟踪运行时代码求值的一个规范设备,从逻辑上讲,执行上下文是用执行上下文栈(栈.调用栈)来维护的. 代码有几种类型:全局代码.函数代码.eval代码和模块代码:每种代码都是在其执行上下文中求值. 当函数被调用时,就创建了一个新的执行上下文,并被压到栈中 - 此时,它变成一个活动的执行上下文.当函数返回时,此上下文被从栈中弹出 function recursive(flag) { // Exit cond

【前端学习笔记】函数柯里化(自网易云课堂)

1. 函数柯里化通常是指把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的并且返回一个接受余下的参数而且返回结果的新函数的技术. // 1. 最简单的柯里化 // sum函数接受三个参数,并返回求和结果 var sum = function(a,b,c) { return a+b+c; } // 最简单柯里化的sum函数 var sum_curry = function(a){ return function(b,c){ return a+b+c; } } 2. 更泛化的定义是