JS查漏补缺——柯里化

69 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情

什么是柯里化

// 我们常见的函数
function add(x, y, z) {
  return x + y + z
}
​
add(1,2,3)

柯里化就是:指的是将一个接受多个参数的函数 变为 接受一个或部分参数返回一个函数的固定形式,这样便于再次调用 上代码:

// 柯里化的函数
function sum(x) {
  return function(y) {
    return function(z) {
      return x + y + z
    }
  }
}
​
add(1)(2)(3)

这样从原来的接收多个参数的函数转 变成 接收一个单一(部分)参数的函数 并 返回接收剩余参数进而返回结果的新函数 感觉好像把原本简单的函数复杂化了,我们加点箭头函数:

// 简化柯里化的代码
var sum = x => y => z => {
  return x + y + z
}
sum(1)(2)(3)

🤔 看起来是变简单了,但将函数柯里化有什么好处呢?我用原来的不香吗? 下面就来讲讲柯里化的好处

为什么要柯里化

好处一:单一职责

在设计模式中就有提到单一职责原则,通俗的说,即一个类只负责一项职责。而我们的柯里化的好处就有一项是单一职责:一个函数处理的问题尽可能的单一。我们可以看到上面柯里化后有很多个函数,就是用这些函数来一个一个地去处理问题,处理完后再在下一个函数中使用处理后的结果

function sum(x){
  x = x + 1
  return function (y){
    y = y * 2
    return x + y
  }
}
​
console.log(sum(1)(2))

当某一层的函数输出结果出现问题的时候就可以到对应的地方去修改就可以了

好处二:逻辑的复用

定制函数,将重复使用的参数定制成一个函数使用,减少代码量和工作量

// 场景:我们经常需要将5与其他数字相加
// 未复用前
function sum(m,n){
  m = m * m
    return m + n
}
console.log(sum(5,10))
console.log(sum(5,100))
console.log(sum(5,1000))
console.log(sum(5,10000))
​
// 柯里化后
function sum(m){
  m = m * m
  return function(n){
    return m + n
  }
}
var add5 = sum(5)
add5(10)
add5(100)
add5(1000)
add5(10000)

自动柯里化函数的实现

目的:通过封装柯里化函数来实现传入一个普通函数自动柯里化的功能

柯里化函数的实现

function myCurrying(fn) {
  // 不确定接收几个参数就用...args来接收
  function curried(...args){
    // 判断当前已经接受的参数的个数,和参数本身需要接受的参数是否已经一致了
    // 一旦接收完后就不再接收了
    if(args.length >= fn.length){
      // 如果调用的时候有绑定this,就直接绑定到fn上
      return fn.apply(this,args)
      // return fn.call(this,args) 也可以
    }else{
      // 新函数接收后面的参数
      function curried2(...args2) {
        // 后来的参数要与之前的函数进行拼接,然后再递归调用
        return curried.apply(this,args.concat(args2))
      }
        return curried2
    }
  }
  return curried
}

自动柯里化函数的使用

// 要进行柯里化的普通函数
function sum(m,n){
  m = m * m
    return m + n
}
​
// 自动柯里化函数的封装
function myCurrying(fn) {
  ...
}
​
var currySum = myCurrying(sum)
console.log(currySum(10)(10)) //110
console.log(currySum(10,10))  //110

普通函数使用柯里化的注意事项

如果是一个一个参数传的话就不能传入多于原本普通函数所需的参数,否则会将前面的看做是一个函数从而报错

// 要进行柯里化的普通函数
function sum(m,n){
  m = m * m
    return m + n
}
​
// 自动柯里化函数的封装
function myCurrying(fn) {
  ...
}
​
var currySum = myCurrying(sum)
console.log(currySum(10)(10)(1))

Snipaste_2022-09-10_10-02-00.png 而如果输入的参数少于原本普通函数所需的参数,则会返回一个接受剩余参数的函数

var currySum = myCurrying(sum)
console.log(currySum(10))
// [Function: curried2]

利用这一特点补充一下柯里化的第三个好处

好处三:延迟运行/计算

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

function currying(fn) {
    var allArgs = []; // 用来接收参数
​
    return function next(...args) {
        var args1 = [].slice.call(args);
​
        // 判断是否执行计算
        if (args1.length > 0) {
            allArgs = allArgs.concat(args1); // 收集传入的参数,进行缓存
            return next;
        } else {
            return fn.apply(null, allArgs); // 符合执行条件,执行计算
        }
    }
}

补充slice( )的说明 : slice() 方法提取某个字符串的一部分,并返回一个新的字符串,且不会改动原字符串。

使用1:

function sum(m, n) {
    m = m * m
    return m + n
}
​
function currying(fn) {
   ...
}
​
var currySum = currying(sum)
console.log(currySum(10)(10)(1)(10)()) // 输出结果:110
// 传入多少个都无所谓,到时候只要需要的参数就行,也不会报错
// 后面一定要加上括号,因为传入空参数是执行的条件,不加则会返回一个接受剩余参数的函数

使用2:

function sum(...args) {
    var m = 0
    for (var i = 0; i < args.length; i++) {
        m += args[i];
    }
    return m;
};
​
function currying(fn) {
    ...
}
​
var currySum = currying(sum)
console.log(currySum(10)(10)(1)(10)()) // 输出结果:31

参考文章: [柯里化与反柯里化_慕课手记]www.imooc.com/article/466…