JavaScript并非函数式编程语言,但是可以在JavaScript中应用函数式编程技术。简单的来说,就是一种编程的风格。
纯函数
概念:
- 相同的输入,一定会产生相同的输出。
- 在函数的执行过程中,没有副作用的产生。
实例解析:
// 实例一
function foo(x) {
return x + 1
}
这里的foo就是最简单的一个纯函数。 函数里面,只依赖于参数 x。
所以无论调用几次,在什么时候调用,都不会影响输出。
// 实例二
let x = 1
function foo(y) {
return x + y
}
这里就不是纯函数了,因为它依赖于外部的变量 x,当外部的变量x发生变化的时候, 就会直接影响到输出。
foo(2) // 3
x = 4
foo(2) // 6
没有相同的输出,就不是纯函数。
// 实例三
function (x) {
// 发送网络请求
const res = fetch(x)
return res
}
这里也不是纯函数,请求返回的数据,会随着外界的变化而变化的。
什么是
副作用(side effect)呢?:改变了外部的环境(变量, localStorage等等)
function foo() {
localStorage.setItem(xxx, xxx)
}
这里也不是纯函数。
纯函数优点:
- 不会因为改变外部的变量而产生bug
- 函数业务逻辑单一
柯里化函数
柯里化函(currying): 是把接受多个参数的函数,变成接受单一参数的函数,并且返回接受剩余参数的新函数。
只传递给函数一部分的参数来调用,让它返回新的函数去处理剩余的参数,这个过程就是柯里化。
function add(x, y, z) {
return x + y + z;
}
// 等价于
function add(x) {
return function(y) {
return function (z) {
return x + y + z;
}
}
}
// 等价于
const add = x => y => z => x + y + z
// 箭头函数的return表达式的省略
当然并不是说,柯里化函数只接受一个参数,也可以接受多个。
function add(x, y) {
return function(z) {
return x + y + z
}
}
实现柯里化函数
首先先了解一个知识点:
function add(x, y, z) {}
add.length // 3
函数的length属性值就是函数接受参数的个数
/**
*
* @param {() => any} fn: 接受的函数
* @returns 返回一个柯里化函数
*/
function autoCurry(fn) {
return function currying(...args1) {
//判断传递参数,跟fn的所需要的参数的长度是否一致
if (fn.length <= args1.length) {
return fn.apply(this, args1)
} else {
// 递归继续执行,跟fn的参数个数相同的时候
return function (...args2) {
return currying.apply(this, [...args1, ...args2])
}
}
}
}
// 使用
function add(x, y, z) {
return x + z + y
}
const curryFn = autoCurry(add)
curryFn(1)(2)(3) // 6
curryFn(1,2)(3) // 6
curryFn(1)(2, 3) // 6
柯里化函数优点
- 每个函数的职责单一,而不是把所有的代码逻辑全部写在一个函数里面
- 缓冲值,可以在下次的调用的时候,使用缓冲值。
funtion add(x, y) {
return x + y
}
const get1 = add(1) // get1函数中,就缓存了1的值
get1(2) // 实现 1 + 2
get1(3) // 实现 1 + 3
组合函数
组合函数: 是JavaScript开发中的一种函数的使用技巧。 针对多个函数,依次执行。
function add(x, y) {
retutn x + y
}
function square(x) {
return **x
}
const num = square(add(1, 2)) // 9
上面的调用过程,就是先执行add函数,然后执行square函数。
那么组合函数,就是用来处理这种情况的:先执行第一个函数,拿到返回的结果值,把结果值作为参数传递给第二个函数,依次类推...
const add = compose(add, square)(1,2)
// 1 和 2作为第一个函数的参数
实现compose函数
/**
*
* @param {...any} fns 函数
* @returns 返回值:any
*/
function compose(...fns) {
const len = fns.length
for (let i = 0; i < len; i++) {
if (typeof fns[i] !== 'function') {
throw new TypeError('Expected a function ')
}
}
return function (...args) {
let res = null;
for (let i = 0; i < len; i++) {
if (i === 0) {
res = len ? fns[i].apply(this, args) : args
} else {
res = fns[i].call(this, res)
}
}
return res
}
}