# JavaScript 柯里化

  • 柯里化也是属于函数式编程里面一个非常重要的概念。

  • 我们先来看一下维基百科的解释:

  • 在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化;

  • 是把接收多个参数的函数,变成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数,而且返回结果的新函数的技术;

  • 柯里化声称 “如果你固定某些参数,你将得到接受余下参数的一个函数”;

  • 维基百科的解释非常的抽象,coderwhy 老师对此的总结:

    • 只传递给函数一部分参数来调用它,让它返回一个函数去处理剩余的参数;
    • 这个过程就称之为柯里化;

# 柯里化过程

未柯里化的函数

function sum(x, y, z) {
  return n1 + n2
}
console.log(sum(11, 22, 33)) // 66

柯里化处理函数

function sum(x) {
  return function (y) {
    return function (z) {
      return x + y + z
    }
  }
}
console.log(sum(11)(22)(33)) // 66

sum 柯里化函数简写

const sum = x => y => z => x + y + z
console.log(sum(11)(22)(33)) // 66

# 让函数的职责单一

  • 为什么需要有柯里化?
    • 在函数式编程中,我们其实往往希望一个函数处理的问题尽可能的单一,而不是将一大堆的处理过程交给一个函数来处理;
    • 那么我们是否就可以将每次传入的参数在单一的函数中进行处理,处理完后在下一个函数中再使用处理后的结果;

每次调用都会执行 x = x * 2 这段代码

function sum(x) {
  x = x * 2
  return function (y) {
    return function (z) {
      return x + y + z
    }
  }
}
console.log(sum(11)(22)(33)) // 77
console.log(sum(22)(33)(44)) // 121

简写,阅读性差不建议

const sum = x => y => z => x * 2 + y + z 
console.log(sum(11)(22)(33)) // 77
console.log(sum(22)(33)(44)) // 121

# 柯里化复用

# 例子 1

这样我们不需要再继续传入 n1 了

function sum(n1) {
  return function (n2) {
    return n1 + n2
  }
}
const sum2 = sum(11)
console.log(sum2(22))
console.log(sum2(33))

简写,阅读性差不建议

const sum = n1 => n2 => n1 + n2
const sum2 = sum(11)
console.log(sum2(22)) // 33
console.log(sum2(33)) // 44

# 例子 2

未柯里化

function log(date, name, food) {
  console.log(`${date.getHours()}:${date.getMinutes()} ${name} 吃了${food}`)
}
log(new Date(), 'nekoaimer', '月亮小饼干')
// 15:25 nekoaimer 吃了月亮小饼干

柯里化

const log = date => name => food => {
  console.log(`${date.getHours()}:${date.getMinutes()} ${name} 吃了${food}`)
}
const nowLog = log(new Date())
nowLog('lain')("椒盐夏虾和夏目葵子")
const nowLog2 = nowLog('saber')
nowLog2('望月夏芽和酒酿梅子')
// 15:25 lain 吃了椒盐夏虾和夏目葵子
// 15:25 saber 吃了望月夏芽和酒酿梅子

# 柯里化函数实现

# 完整写法

"use strict"
function myCurrying(fn) {
  function curried(...args) {
    // 判断当前已经接收的参数的个数,可以参数本身需要接受的参数是否已经一致了
    // 1. 当已经传入的参数 大于等于 需要的参数时,就执行函数
    if (args.length >= fn.length) {
      // fn(...args)
      // fn.call(this, ...args)
      return fn.apply(this, args)
    } else {
      // 2. 没有达到个数时,需要返回一个新的函数,继续来接收的参数
      return function (...args2) {
        // 3. 接收到参数后,需要递归调用 curried 来检查函数的个数是否达到
        return curried.apply(this, args.concat(args2))
      }
    }
  }
  // 4. 返回一个新的函数
  return curried
}
function add(x, y, z) {
  return x + y + z
}
var curryAdd = myCurrying(add)
console.log(curryAdd(10, 20, 30)) // 60
console.log(curryAdd(10, 20)(30)) // 60
console.log(curryAdd(10)(20, 30)) // 60
 
console.log(curryAdd(10)(20)(30)) // 60

# 优化写法

"use strict"
Function.prototype.myCurrying = function (fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args)
    } else {
      return function (...args2) {
        return curried.apply(this, [...args, ...args2])
      }
    }
  }
}
function add(x, y, z) {
  return x + y + z
}
var curryAdd = Function.myCurrying(add)
console.log(curryAdd(10, 20, 30)) // 60
console.log(curryAdd(10, 20)(30)) // 60
console.log(curryAdd(10)(20, 30)) // 60
 
console.log(curryAdd(10)(20)(30)) // 60

# bind 妙用

这是我认为比较优雅的写法!~

"use strict"
Function.prototype.myCurrying = function (fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args)
    } 
    return curried.bind(this, ...args)
  }
}
function add(x, y, z) {
  return x + y + z
}
var curryAdd = Function.myCurrying(add)
console.log(curryAdd(10, 20, 30)) // 60
console.log(curryAdd(10, 20)(30)) // 60
console.log(curryAdd(10)(20, 30)) // 60
 
console.log(curryAdd(10)(20)(30)) // 60

# 组合函数实现

  • 组合(Compose)函数是在 JavaScript 开发过程中一种对函数的使用技巧、模式:
    • 比如我们现在需要对某一个数据进行函数的调用,执行两个函数 mul 和 square,这两个函数是依次执行的;
    • 那么如果每次我们都需要进行两个函数的调用,操作上就会显得重复;
    • 那么是否可以将这两个函数组合起来,自动依次调用呢?
    • 这个过程就是对函数的组合,我们称之为 组合函数(Compose Function);
function mul(m) {
  return m * 2
}
function square(n) {
  return n ** 2
}
function composeFn(...fns) {
  for (const k in fns) {
    if (!fns[k] instanceof Function) {
      throw new TypeError("Expected arguments are functions")
    }
  }
  return function (...args) {
    console.log(length);
    let i = 0
    let result = fns.length ? fns[i].apply(this, args) : args
    while (i++ < fns.length) {
     return result = fns[i].call(this, result)
    }
  }
}
const newFn = composeFn(mul, square)
  
console.log(newFn(10)) // 400