科里化函数

489 阅读2分钟

初始科里化

刚认识到科里化函数是从一道面试题开始:
add(1)(2)(3)(4) = 10;
  • 疑问:没见到过这题的小伙伴肯定很懵逼, 怎么函数add(1)后面还能跟(2)(3)(4)
  • 原理就是add(1)返回一个函数。因此可以拼接上(2),继续执行直达最后
  • 如下图的函数结构所示
function add (a) {
	return function (b) {
		return function (c) {
			return function (d) {
				return a + b + c + d
			}
		}
	}
}
  • 对于程序猿来说这种方法肯定是不美丽的,那么就用科里化的函数类解决这个问题了
  • 来看看科里化是怎么解决的吧
  • 原理:利用闭包,将一个函数接受多个参数变成一步步接受单个或多个参数
       //定长的时候
       //定长:当传入的参数的个数提前规定了的
      function fn(a,b,c,d){
      	//reduce函数的用法,将参数相加并且返回
        return  [...arguments].reduce((a,b)=>a+b)
      }
	//对函数进行科里化封装
      function currying(fn){
        //获得定长的数量,fn(a,b,c,d)来说就是4个,len = 4;
        let len = fn.length;
        //这里存储传入参数的集合
        let args = []
        //闭包现象
        return function _c(){
          //参数整合
          args = [...args,...arguments];
          if(args.length<len){
          //注意这里只能return 函数名,为了后面直接连接(2)=>_c(2)
            return _c
          }else{
          //如果参数到4个了,就显示绑定this,用apply来执行
            return fn.apply(this,args)
          }
        }
      }
      
      //对函数进行封装
      let add = currying(fn);
      //执行res = 10
      let res = add(1)(2)(3)(4);
  • 那么第一种是定长的情况,那么不定长呢?
  • 对于不定长,我们设定一个边界条件:即传入的参数第一次为空就代表参数接收完成
	//与上面第一个函数式一致
       function fn2(){
        return [...arguments].reduce((a,b)=>a+b)
      }
      function currying2(){
        let args = [];
        return function _c(){
          args = [...args,...arguments];
          //即传入的参数第一次为空就代表参数接收完成
          if(arguments.length){
            return _c
          }else{
            return fn.apply(this,args)
          }
        }
      }
      let add2 = currying2(fn2);
      //这里最后参数为空的时候才会执行fn.apply(this,args)
      // res2 = 24
      let res2 = add2(1)(2,3,9,9)()

科里化函数的应用

  • 那么讲了原理,让我们来看看实际程序中这个函数的使用

参数复用/延迟运行

  • 可以提前传入一个指定的参数,将返回函数(这个函数就可以复用)传入另外的参数,
  • 同时这个函数也达到了了延迟运行的目的
  • bind也用了currying函数的思想
// 正常正则验证字符串 reg.test(txt)

// 函数封装后
function check(reg, txt) {
    return reg.test(txt)
}

check(/\d+/g, 'test')       //false
check(/[a-z]+/g, 'test')    //true

// Currying后
function curryingCheck(reg) {
    return function(txt) {
        return reg.test(txt)
    }
}

var hasNumber = curryingCheck(/\d+/g)
var hasLetter = curryingCheck(/[a-z]+/g)

hasNumber('test1')      // true
hasNumber('testtest')   // false
hasLetter('21212')      // false
hasLetter('21212')      // false