🍏 JavaScript 函数式编程相关
相关问题:
- 对 JavaScript 函数式编程的理解?
- JavaScript 函数式编程的优缺点?
# 概念
JavaScript 函数式编程是一种「编程范式」(Programming Paradigm),一种编写程序的方法论。
主要的编程范式有三种:命令式编程、声明式编程以及函数式编程。
相比于命令式编程,函数式编程 更加强调程序的执行结果而非执行的过程,倡导利用若干的简单的执行单元让计算结果不断渐进,逐渐推导复杂的运算,而非设计一个复杂的执行过程。
🌰 例子 / 命令式编程与函数式编程比较:
// 命令式编程
var array = [0, 1, 2, 3]
for (let i = 0; i < array.length; i++) {
array[i] = Math.pow(array[i], 2)
}
// 函数式编程
[0, 1, 2, 3].map(num => Math.pow(num, 2))
2
3
4
5
6
7
8
简单来说,函数式编程就是将过程逻辑写成函数,定义好输入参数,只关心它的输出结果。即是一种描述集合与几何之间的转换关系,输入通过函数都会返回有且只有一个值。
函数实际上是一个关系(映射),这个关系是可以组合的。一旦知道函数的输出类型可以匹配一个函数的输入,就将它们可以进行组合。
# 纯函数
函数式编程旨在尽可能提高代码的无状态性和不变形。要做到这一点就要使用 无副作用的函数,也就是纯函数。
纯函数是给定输入返还相同输出的函数,并且要求所有的数据都是不可变的。即 纯函数 = 无状态 + 数据不可变。
🌰 例子:
let double = value => value * 2
纯函数的特点:
- 函数内部传入指定的值,就会返回确定唯一的值;
- 不会造成超出作用域的变化,例如修改全局变量或引用传递的参数;
纯函数的优势:
- 使用纯函数可以产生可测试的代码;
- 不依赖外部环境计算,不会产生副作用,提高函数的可复用性;
- 代码可读性强,函数不管是否是一个纯函数都会有一个语义化名称,便于理解阅读;
- 可以组装成复杂任务的可能性,符合模块化的概念以及单一职责原则。
# 高阶函数
在编程世界中,只需要处理的是 「数据」 和「关系」,而关系就是函数。
编程工作也是在找一种关系,一旦关系找到了,问题就解决了。剩下的事情就是让数据流过这种关系,转换成另一种数据。
高阶函数的作用,就是以函数作为输入或者输出的函数。通过高阶函数 抽象过程,注重结果。
🌰 例子:
const forEach = function(arr, fn) {
for(let i = 0; i < arr.length; i++) {
fn(arr[i])
}
}
let arr = [1, 2, 3]
forEach(arr, (item) => {
console.log(item)
})
2
3
4
5
6
7
8
9
10
forEach
是一个高阶函数,抽象了循环如何做的逻辑,然后直接关注做了什么。
高阶函数存在缓存的特性,主要利用闭包。
🌰 例子:
const once = (fn) => {
let done = false
return function() {
if(!done) {
fn.apply(this, fn)
} else {
console.log("该函数已执行")
}
done = true
}
}
2
3
4
5
6
7
8
9
10
11
# 柯里化
讲一个多参数函数转化成一个嵌套的一元函数的过程。
柯里化函数的意义:
- 让纯函数更纯。每接受一个参数,松散结构。
- 惰性执行。
🌰 例子:
let fn = (x, y) => x + y
转换为柯里化函数:
let fn = (x, y) => x + y
const curry = function(fn) {
return function(x) {
return function(y) {
return fn(x, y)
}
}
}
let myfn = curry(fn)
console.log(myfn(1)(2))
2
3
4
5
6
7
8
9
10
11
🌰 例子 / 多参数的柯里化函数:
const curry = function(fn) {
return function curriedFn(...args) {
if (args.length < fn.length) {
return function() {
return curriedFn(...args.concat([...arguments]))
}
}
return fn(...args)
}
}
const fn = (x, y, z, a) => x + y + z + a
const myfn = curry(fn)
console.log(myfn(1)(2)(3)(4))
2
3
4
5
6
7
8
9
10
11
12
13
14
# 组合与管理
组合函数与管道函数的意义在于将多个函数组合成一个函数,完成更复杂的逻辑。
🌰 例子:
function afn(a) {
return a * 2
}
function bfn(b) {
return b * 3
}
const compose = (a, b) => c => a(b(c))
let myfn = compost(afn, bfn)
console.log(myfn(2))
2
3
4
5
6
7
8
9
10
11
compose
实现了一个简单的功能:形成了一个新的函数。而这个函数就是一条从afn
->bfn
的流水线。
实现一个多函数组合:
const compose = (...fns) => val => fns.reverse().((acc, fn) => fn(acc), val)
compost
从右到左执行。
管道函数执行顺序从左到右:
const pipe = (...fns) => val => fns.reduce((acc, fn) => fn(acc), val)
# 优缺点
函数式编程的优点:
- 更好的管理状态。宗旨是无状态,或者更少的状态,能最大化的减少这些未知、优化代码、减少出错的情况;
- 更简单的复用。固定输入 和 固定输出,没有其他外部变量的影响,并且没有副作用。这样代码复用的时候,完全不需要考虑它的内部实现和外部影响;
- 更优雅的组合。在网页中可以由各个组件组成;一个函数可以由多个小函数组成。更强的复用性,能带来更强大的组合性。
- 隐性好处。减少代码量,提高维护性。
函数式编程缺点:
- 性能。函数式编程相对于指令时编程,性能是一个短板,因为它往往会对一个方法进行过度包装,从而产生上下文切换的性能开销。
- 资源占用。在 JavaScript 中为了实现对象状态的不可变,往往会创建新的对象,因此对于垃圾回收所产生的压力远远超过其他编程方式。
- 递归陷阱:在函数式编程中,为了实现迭代,通常会采用递归操作。