函数式编程
2022年6月13日大约 3 分钟
表达式:运算过程,总是有返回值
语句:执行操作,没有返回值
函数式编程多用表达式,少用语句。
纯函数:
- 相同输入产生相同输出(没有依赖除了输入的外部环境)
- 无副作用(没有改变包括输入在内的外部环境)
数组的方法:
- slice 截取纯函数:返回新数组,不会修改原数组,纯函数
- splice 拼接非纯函数:返回被删的数组,会修改原数组,非纯函数
纯函数判断
// 纯函数
function sum(num1, num2) {
return num1 + num2
}
// 非纯函数(依赖 dep)
let dep = 5
function add(num){
return dep + num
}
console.log(add(5)) // 10
dep = 10
console.log(add(5)) // 15
// 非纯函数(修改了输入)
let person = { name: 'nevermore', age: 23 }
function modify(obj) {
obj.age = 100
}
modify(person)
console.log(person)
柯里化
柯里化(Currying):只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。每次调用函数时,它只接受一部分参数,并返回一个函数,直到传递所有参数为止。
函数的单一职责原则:每个函数的处理逻辑单一
柯里化例子
function sum1(x, y, z) {
return x + y + z
}
console.log(sum1(10, 20, 30)) // 60
function sum2(x) {
return function (y) {
return function (z) {
return x + y + z
}
}
}
console.log(sum2(10)(20)(30)) // 60
// 简化柯里化的代码
const sum3 = (x) => (y) => (z) => {
return x + y + z
}
console.log(sum3(10)(20)(30)) // 60
// 再省略 return
const sum4 = (x) => (y) => (z) => x + y + z
console.log(sum4(10)(20)(30)) // 60
柯里化的作用
- 参数复用:公共参数
- 提前返回:只接收一部分参数,返回一个函数
- 延迟执行:柯里化返回一个预置参数的新函数,并没有立即执行
参数复用例子:
function log(date, type, message) {
console.log(
`[${date.getHours()}:${date.getMinutes()}][${type}]: [${message}]`
)
}
// 柯里化实现了参数的复用
// log(new Date(), 'FIX', '修复某 bug')
// log(new Date(), 'FIX', '修复某某 bug')
// log(new Date(), 'FEATURE', '添加某功能')
let nowLog = log(new Date())
nowLog('FIX')('修复某 bug')
nowLog('FEATURE')('添加某功能')
let nowFixLog = log(new Date())('FIX')
nowFixLog('修复某 bug')
let nowFeatureLog = log(new Date())('FEATURE')
nowFeatureLog('添加某功能')
柯里化函数的实现
柯里化函数:将普通函数柯里化的一个工具函数
四行代码实现:
- 判断当前传入函数的实参个数
args.length
是否大于等于需要柯里化的函数形参个数fn.length
- 是则结束执行
fn(...args)
- 否则递归执行一个返回
curry(fn, ...args, ..._args)
函数的函数
// 柯里化函数
const curry = (fn, ...args) =>
args.length >= fn.length
? fn(...args)
: (..._args) => curry(fn, ...args, ..._args)
// 箭头函数转换成普通函数
const curry = function (fn, ...args) {
if (args.length >= fn.length) {
return fn(...args)
} else {
return function (..._args) {
return curry(fn, ...args, ..._args)
}
}
}
// 验证
function sum(x, y, z) {
return x + y + z
}
const curriedSum = curry(sum)
console.log(curriedSum(1, 2, 3)) // 6
console.log(curriedSum(1)(2)(3)) // 6
console.log(curriedSum(1, 2)(3)) // 6
console.log(curriedSum(1)(2, 3)) // 6
组合函数
组合函数:将一系列单一责任函数组合成一个复杂函数,自动化执行一系列函数。
const add1 = (x) => x + 1
const mul2 = (x) => x * 2
const pow3 = (x) => x ** 3
const div4 = (x) => x / 4
// 数据流从右至左
div4(pow3(mul2(add1(0)))) // 2
compose可以把类似于f(g(h(x)))
的写法简化成compose(f, g, h)(x)
,提高代码可读性,数据流从左至右称为管道。
// 组合函数(从左至右)
const compose = (...fns) => {
return (x) => {
return fns.reduce((arg, fn) => fn(arg), x)
}
}
// 箭头函数转化为普通函数
function compose(...fns) {
return function (x) {
// 若要数据流从右至左,则可以用 reduceRight
return fns.reduce(function (arg, fn) {
return fn(arg)
}, x)
}
}
// 验证
const add1 = (x) => x + 1
const mul2 = (x) => x * 2
const pow3 = (x) => x ** 3
const div4 = (x) => x / 4
compose(add1, mul2, pow3, div4)(0) // 2