# 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 |