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