JavaScript 函数式编程基础

函数式编程是一种编程范式,它将计算机运算视为函数的计算。

函数式编程语言语言最重要的基础是 $\lambda$ 演算。JavaScript 是一种十分灵活的编程语言,其可以算是多范式语言,即支持函数式编程、命令式编程、对象式编程等。

函数式编程涉及许多数学概念,不同于过程式编程和对象式编程,由其名字即可看出,在函数式编程中,函数是一等公民。函数式编程涉及的概念有:

  • 闭包
  • 高阶函数
  • 柯里化
  • 偏应用
  • 组合与管道
  • 函子
  • Monad

函数式编程定义

函数式编程是将函数作为编程的基础,并且函数总是接受参数,返回一个值,并且遵循相同的参数总是输出相同的值。函数应该依据接收到的参数运行,不依赖外部环境的运行,即函数必须是纯函数

特性:

  1. 引用透明性
    • 即相同的参数总是输出相同的值
  2. 主张命令式编程、编写抽象的代码
    • 利于重用代码
  3. 纯函数
    • 遵循相同的参数总是输出相同的值,且函数应该依据接收到的参数运行,不依赖外部环境的运行
    • 有利于代码测试
  4. 可实现并发代码
  5. 可缓存
  6. 支持管道与组合,类似于 linux 命令的函数式组合命令

闭包与高阶函数

高阶函数是用于抽象通用代码的问题,即高阶函数就是定于抽象函数。这种抽象不仅可以使使用者不必在意其实现细节即可使用,且便于测试代码。

例如:实现一个 forEach 遍历数组,并对每一个item执行给定的函数,用户只需给出数组和执行函数即可;注意,实现的函数与数组原生方法作用一致,但是 .forEach() 的执行方式是声明式的而不是函数式的。

1
2
3
4
5
const forEach (arr, func) => {
for (let i = 0; i < arr.length; i++) {
func(arr[i]);
}
}

也可以封装一个遍历对象的函数 eachObject

1
2
3
4
5
6
7
const eachObject = (obj, func) => {
for (let p in obj) {
if (obj.hasOwnProperty(p)) {
func(p, obj[p])
}
}
}

一些实用的高阶函数:

every

用于检查数组内所有值都符合给定的函数返回 true

1
2
3
4
5
6
7
const every = (arr, func) => {
let ans = true;
for (let i = 0; i < arr.length; i++) {
ans = ans && func(arr[i]);
}
return ans;
}

some or any

与 every 函数类似,查找数组内是否有值符合给定的函数返回 true

1
2
3
4
5
6
7
8
const some = (arr, func) => {
let ans = true;
for (let i = 0; i < arr.length; i++) {
ans = ans || func(arr[i]);
}
return ans;
}
const any = some; // 别名

sortBy

js数组内置的sort接收一个函数作为排序的依据,这个函数是个很公式化的函数,可以使用高阶函数抽象一下。

1
2
3
4
5
6
7
const sortBy = (property) => {
return (a, b) => {
if (a[property] < b[property]) return -1;
if (a[property] > b[property]) return 1;
return 0;
}
}

例如,有个二维数组 arr = [[1,15], [4, 6], [7, 8]],可以使用 arr.sort(sortBy(1)) 来排序。

tap

用于调试数据的函数

1
2
3
4
5
const tap = val => func => {
if (typeof func === 'function') {
return func(val);
}
}

unary

当使用类似于 ['1', '2', '3'].map(parseInt) 是,并不会返回我们所期望的值:[1,2,3],而是会返回 [1, NaN, NaN] 。原因在于 parseInt 函数可能接收2个参数map将数组的index作为参数传递给parseInt函数了:

1
2
3
parseInt('1', 0) // 1, 第二个参数为0,不合规,按默认10进制转换数字
parseInt('2', 1) // NaN
parseInt('3', 2) // NaN

如果需要解决以上问题,可以使用 arr.map(args => parseInt(args))

我们可以抽象一个函数用于解决以上问题,判断给定的函数接受几个参数,以不同的方式调用返回:

1
const unary = func => func.length === 1 ? func : args => func(args)

现在可以直接使用:arr.map(unary(parseInt))

once

在某些情况下,某些函数只需要调用一次,为了防止其多次调用,可以封装一个函数:

1
2
3
4
5
6
7
8
9
const once = func => {
let done = false;
return (...args) => {
if (!done) {
done = true;
return func.apply(this, args);
}
}
}
JavaScript 函数式编程
作者:Kart Jim
链接:https://github.com/can-dy-jack/delicate/2023/02/05/js函数式编程/JavaScript 函数式编程01-基础/
来源:Hexo
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 许可协议。著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
长风破浪会有时,直挂云帆济沧海