30天精通RxJS(02):Functional Programming基本观念

本文深入浅出地解析函数式编程(FP)的核心概念,包括其编程范式、基本要件如函数为一等公民,以及重要特性如纯函数、引用透明等。并通过实例对比说明FP的优势,如高可读性、易于并行处理等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原作者:一名來自台灣的小伙子,熱愛學習新技術,喜歡 JS 與 Functional Programming,熱衷於把困難的技術用簡單的語言闡述,歡迎來到我的文章。

原文

什么是Functional Programming?

Functional Programming是一种编程规范(programming paradigm),就像Object-oriented Programming(OOP)一样,就是一种写程序的方法论,这些方法论告诉我们如何思考及解决问题。

简单说Functional Programming核心思想就是做运算处理,并用function来思考问题,例如像以下的算数表达式:

(5 + 6) - 1 * 3
复制代码

我们可以写成

const add = (a, b) => a + b
const mul = (a, b) => a * b
const sub = (a, b) => a - b

sub(add(5, 6), mul(1, 3))
复制代码

我们把每个运算包成一个个不同的function,并用这些function组合出我们要的结果,这就是最简单的Functional Programming。

Functional Programming 基本要件

跟OOP一样不是所有的语言都支持FP,要能够支持FP的语言至少需要符合函数为一等公民的特性。

函数为一等公民(First Class)

一等公民就是指跟其他数据类型具有同等地位,也就是说函数能够被赋值给变量,函数也能够被当作参数传入另一个函数,也可当作一个函数的返回值

函数能够被赋值给变量
var hello = function() {}
复制代码
函数能被当作参数传入
fetch('www.google.com')
.then(function(response) {}) // 匿名 function 被传入 then()
复制代码
函数能被当作返回值
var a = function(a) {
	return function(b) {
	  return a + b;
	}; 
	// 可以返回一个 function
}
复制代码

Functional Programming重要特性

Expression, no Statement

Functional Programming都是表达式(Expression)不会是声明式(Statement)。基本区分表达式与声明式:

表达式是一个运算过程,一定会有返回值,例如执行一个function

add(1,2)
复制代码

声明式 则是表现某个行为,例如一个赋值给一个变数

a = 1;
复制代码

有时候表达式也可能同时是合法的声明式,这里只讲基本的判断方法。如果想更深入了解其中的差异,可以看这篇文章 Expressions versus statements in JavaScript

由于Functional Programming最早就是为了做运算处理不管I/O,而Statement通常都属于对系统I/O的操作,所以FP很自然的不会是Statement。

当然在事务中不可能完全没有I/O的操作,Functional Programming只要求对I/O操作限制到最小,不要有不必要的I/O行为,尽量保持运算过程的单纯。

Pure Function(纯函数)

Pure function是指一个function给予相同的参数,永远会回传相同的返回值,并且没有任何显著的副作用(Side Effect)

举个例子:

var arr = [1, 2, 3, 4, 5];

arr.slice(0, 3); // [1, 2, 3]

arr.slice(0, 3); // [1, 2, 3]

arr.slice(0, 3); // [1, 2, 3]
复制代码

这里可以看到slice不管执行几次,返回值都是相同的,并且除了返回一个值(value)之外并没有做任何事,所以slice就是一个pure function。

var arr = [1, 2, 3, 4, 5];

arr.splice(0, 3); // [1, 2, 3]

arr.splice(0, 3); // [4, 5]

arr.slice(0, 3); // []
复制代码

这里我们换成用splice,因为splice每执行一次就会影响arr的值,导致每次结果都不同,这就很明显不是一个pure function。

Side Effect(副作用)

Side Effect是指一个function做了跟本身运算返回值没有关系的事,比如说修改某个全局变数,或是修改传入参数的值,甚至是执行console.log都算是Side Effect。

Functional Programming强调没有Side Effect,也就是function要保持纯粹,只做运算并返回一个值,没有其他额外的行为。

这里列举几个前端常见的Side Effect,但不是全部

  • 发送http request

  • 在画面输出或是log

  • 获得使用者input

  • Query DOM对象

Referential transparency(引用透明)

前面提到的pure function不管外部环境如何,只要参数相同,函数执行的返回结果必定相同。这种不依赖任何外部状态,只依赖于传入的参数的特性也称为引用透明(Referential transparency)

利用参数保存状态

由于最近很红的Redux使我能很好的举例,让大家了解什么是用参数保存状态。了解Redux的开发者应该会知Redux的状态是由各个reducer所组成的,而每个reducer的状态就是保存在参数中!

function countReducer(state = 0, action) {
// ...
}
复制代码

如果你跟Redux不熟可以看下面递回的例子

function findIndex(arr, predicate, start = 0) {
    if (0 <= start && start < arr.length) {
        if (predicate(arr[start])) {
            return start;
        }
        return findIndex(arr, predicate, start+1);
    }
}
findIndex(['a', 'b'], x => x === 'b'); // 找数组中 'b' 的 index
复制代码

这里我们写了一个findIndex用来找数组中的元素位置,我们在findIndex中故意多塞了一个参数用来保存当前找到第几个index的状态,这就是利用参数保存状态!

这边用到了递归,递归会不断的调用自己,制造多层stack frame,会导致运算速度较慢,而这通常需要靠编译器做优化!

那JS有没有做递归优化呢?恭喜大家,ES6提供了尾调用优化(tail call optimization),让我们有一些手法可以让递归更有效率!

Functional Programming优势

可读性高

当我们通过一系列的函数封装数据的操作过程,代码能变得非常的简洁且可读性极高,例如下面的例子

[9, 4].concat([8, 7]) // 合并数组
      .sort()  // 排序
      .filter(x => x > 5) // 过滤出大于 5 的
复制代码

可维护性高

因为Pure function等特性,执行结果不依赖外部状态,且不会对外部环境有任何操作,使Functional Programming能更好的排错及编写单元测试。

易于并行/平行处理

Functional Programming易于做并行/平行(Concurrency/Parallel)处理,因为我们基本上只做运算不碰I/O,再加上没有Side Effect的特性,所以较不用担心deadlock等问题。

今日小结

今天讲了Functional Programming的基本特性,及其优势。现在愈来愈多的Library用到了FP的观念,JS也越来越多Functional的函数库,例如:Lodash,Underscore,lazy,Ramda。了解FP的基本观念有助于我们在学习其他Library更容易上手,也能使我们写出更好的代码,希望各位读者有所收获,若有任何疑问欢迎在下方留言给我!

转载于:https://juejin.im/post/5cd6c57ff265da039257ffc4

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值