函数式编程思想

相关问题

什么是纯函数?为什么它们在函数式编程中很重要?

纯函数是一种函数,其返回值仅由其输入参数决定,不产生任何可观察的副作用,如修改全局对象或外部状态。纯函数具有以下特性:

  • 确定性:相同的输入永远得到相同的输出。
  • 无副作用:函数执行过程中不会改变任何外部状态。

纯函数在函数式编程中非常重要,因为它们提供了可预测性和可测试性。由于纯函数不依赖于且不修改外部环境的状态,它们在并发环境下特别有用,可以减少在多线程环境中由状态改变引起的错误。此外,纯函数的可组合性使得开发者能够构建更加模块化和可重用的代码。

解释函数式编程中的高阶函数并给出一个例子

高阶函数是至少满足下列一个条件的函数:

  • 接受一个或多个函数作为输入。
  • 返回一个函数作为输出。

高阶函数在函数式编程中用于抽象或隔离行为,不直接操作数据,而是创建包含操作的函数,这增强了代码的可重用性和模块化。例如,.map()和.filter()都是数组的高阶函数,因为它们接受一个函数作为参数。

函数式编程思想

函数式编程思想vs 面向对象编程思想

  • 面向对象编程(00P)是一种强大的编程范式,它使用类和对象来组织代码,以模拟现实世界的实体和交互。尽管 OOP 在许多方面非常有用,例如在编写大型软件系统时提供了清晰的模块化结构,但它也存在一些问题,特别是与状态管理和副作用相关的问题。
  • 函数式编程,关注的是函数,我们在封装设计的时候,要将问题的求解方法抽离到各个函数中,通过**函数组合 **提供问题的解法

面向对象编程存在的问题

  • 状态管理复杂
    • 在OOP 中,对象通常包含状态(数据),状态可以通过方法(行为)来改变。当应用程序复杂时,对象的状态可能在多个方法中被不同的方式改变,导致状态管理变得复杂和困难。
  • 副作用和可预测性
    • 对象方法可能会改变全局状态或者对象的内部状态,这些副作用会使得程序的行为变得不可预测和难以理解。
  • 继承的问题
    • 继承可以导致高度耦合的代码,使得修改和理解代码更加困难。子类依赖于父类的实现细节,父类的改动可能会影响到所有子类。

:::info 面向对象状态管理和UI逻辑,复用性差

副作用,不可预测

继承

:::

函数式编程思想能够解决什么问题

  • 不可变性
    • 数据结构是不可变的,这意味着一旦创建,数据就不能更改。所有的变化都通过返回新的数据副本来实现。
  • 无副作用的函数
    • 函数不修改外部状态,它们只依赖于输入参数,返回新的数据而不是修改已有数据
  • 使用纯函数
    • 纯函数增加了代码的可测试性和可预测性。

函数式编程核心概念

  • 函数为一等公民
  • 纯函数(Pure Functions)
  • 不可变性 (Immutability)
  • 函数组合 (Function Composition)
  • 高阶函数(Higher-Order Functions)

函数为一等公民 (First-Class Functions)

:::info 当函数在一个语言中被视为一等公民时,这意味着函数可以像任何其他数据类型一样被传递和操作。它们可以作为变量存储、作为参数传递给其他函数、作为其他函数的返回值或者赋值给对象的属性。

:::

纯函数 (Pure Functions)

:::info 纯函数是这样一种函数,它的返回值仅由其输入参数决定,并且在执行过程中不产生副作用(如修改外部变量或对象状态)

:::

不可变性(Immutability)

:::info 不可变性指的是数据状态一旦创建,就不能被改变。在函数式编程中,任何数据变更都应通过创建和返回新的数据副本来实现,而不是直接修改原始数据。

:::

函数组合 (Function Composition)

:::info 函数组合是将两个或多个函数组合成一个单一函数的过程。在组合中,一个函数的输出成为另一个函数的输入。

:::

高阶函数(Higher-Order Functions)

:::info 高阶函数是指至少满足以下一个条件的函数:接受一个或多个函数作为参数,或者返回一个函数作为结果。

:::

纯函数

什么是纯函数?

:::color3 相同的入参,得到相同的结果,且无副作用

:::

纯函数是这样一种函数,即相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用。

比如数组的 slice 和 splice,这两个函数的作用并无二致---一但是注意,它们各自的方式却大不同,但不管怎么说作用还是一样的。

  • slice 符合纯函数的定义:因为对相同的输入它保证能返回相同的输出;
  • splice 却不同:会产生可观察到的副作用,即这个数组永久地改变了;

副作用

"副作用"的关键部分在于"副"。就像一潭死水中的"水"本身并不是幼虫的培养器,"死"才是生成虫群的原因。同理,副作用中的"副"是滋生 bug的温床。

副作用是在计算结果的过程中,系统状态的一种变化,或者与外部世界进行的可观察的交互。副作用可能包含,但不限于:

  1. 更改文件系统;

  2. 往数据库插入记录;

    3.发送一个 http 请求;

  3. 可变数据;

  4. 打印/log;

  5. 获取用户输入;

  6. DOM 查询;

  7. 访问系统状态...

概括来讲,只要是跟函数外部环境发生的交互就都是副作用---一这一点可能会让你怀疑无副作用编程的可行性。函数式编程的哲学就是假定副作用是造成不正当行为的主要原因。

这并不是说,要禁止使用一切副作用,而是说,要让它们在可控的范围内发生。

副作用让一个函数变得不纯是有道理的:从定义上来说,纯函数必须要能够根据相同的输入返回相同的输出;如果函数需要跟外部事物打交道,那么就无法保证这一点了。

纯函数的特点

可缓存性(Cacheable)

  • 首先,纯函数总能够根据输入来做缓存。实现缓存的一种典型方式是 memoize 技术

可移植性 / 自文档化 (Portable / Self-Documenting)

  • 纯函数是完全自给自足的,它需要的所有东西都能轻易获得。仔细思考思考这一点⋯.这种自给自足的好处是什么呢?

可测试性(Testable)

  • 纯函数让测试更加容易。我们不需要伪造一个"真实的"支付网关,或者每一次测试之前都要配置、之后都要断言状态 (assert the state)。只需简单地给函数一个输入,然后断言输出就好了。

合理性(Reasonable)

  • 很多人相信使用纯函数最大的好处是引用透明性 (referential transparency)。如果一段代码可以替换成它执行所得的结果,而且是在不改变整个程序行为的前提下替换的,那么我们就说这段代码是引用透明的。

并行代码

  • 我们可以并行运行任意纯函数
  • 因为纯函数根本不需要访问共享的内存,而且根据其定义,纯函数也不会因副作用而进入竞争态(race condition).
  • 并行代码在服务端js 环境以及使用了 web worker 的浏览器那里是非常容易实现的,因为它们使用了线程 (thread)。不过出于对非纯函数复杂度的考虑,当前主流观点还是避免使用这种并行。

函数柯里化

:::color3 将多参数转为一系列单参数函数的过程

:::

柯里化是函数式编程中的一个技术,它涉及将一个多参数的函数转换成一系列使用一个参数的函数。柯里化的函数通常返回另一个接受剩余参数的函数,这个过程一直持续,直到所有参数都被消耗掉。

柯里化的用途

  • 参数复用:柯里化可以创建可以被多次调用的函数,每次调用使用部分参数来执行任务,这有助于減少重复代码和提高代码复用
  • 延迟计算:通过柯里化,函数的执行可以被延迟。只有当接收到所有必要的参数之后,才会进行计算
  • 动态生成函数:可以根据柯里化的中间步骤动态生成新的函数,这些函数可以适用于特定的情况,从而增强了函数的适应性和灵活性
  • 简化函数的调用:柯里化有助于减少函数调用时需要的参数数量,使得函数调用更加简洁

函数组合

函数式编程中的函数组合(Function Composition)是一个核心概念,它允许我们将多个函数链接在一起,创建出新的函数。这种方法的强大之处在于它提供了一种模块化方式来构建程序,使得每个部分都可以独立测试和重用。

特性

  • 模块性:通过将小而专一的函数组合成复杂的行为,增强代码的模块性。
  • 可读性:适当的函数组合可以使代码更加直观和易于理解。
  • 复用性:独立的函数可以在多个地方被复用,減少代码重复。
  • 声明性:通过组合方式,代码更加声明性,聚焦于"做什么"而非"怎么做"。

不可变性

在函数式编程中,不可变性是一个核心概念,指的是数据一旦被创建就不能改变。任何对数据的修改或更新操作都会生成一个新的数据副本,而不是改变原有的数据。这种做法有助于避免副作用和数据状态的不一致性,使得程序的行为更可预测,简化了复杂程序中的状态管理。这一特性在 React 中用途颇广。单向数据流状态的更改需要我们使用这一特性来实现。

不可变性的优势

提高程序的可预测性:由于数据不会被意外修改,函数的行为和输出更加可预测。

简化并发编程:不可变数据结构自然是线程安全的,因为没有线程可以修改数据,从而避免了锁和竞态条件的需求。

便于历史数据追踪:保持历史版本的数据不被改变,可以轻松地实现撤销和重做功能。

React 中的不可变性

在 React 中,组件的状态应当是不可变的。这意味着在更新状态时,你应该创建状态的一个新副本而不是直接修改状态。这种做法有助于优化 React 应用的性能,尤其是在利用 shouldComponentUpdate、PureComponent 或 React.memo等性能优化手段时,因为 React 可以快速地检查 props 或 state 是否发生变化,从而決定是否需要重新渲染组件。

Immer 使用

Immer 是一个流行的库,用于简化不可变数据结构的处理。它允许你编写看似"可变"的代码,但实际上会产生不可变的数据更新。在 React 中,使用Immer 可以简化复杂状态的更新逻辑。

相关资料

相关推荐
PBitW3 分钟前
工作中突然发现零宽字符串的作用了!
前端·javascript·vue.js
VeryCool4 分钟前
React Native新架构升级实战【从 0.62 到 0.72】
前端·javascript·架构
小小小小宇6 分钟前
JS匹配两数组中全相等对象
前端
xixixin_8 分钟前
【uniapp】uni.setClipboardData 方法失效 bug 解决方案
java·前端·uni-app
狂炫一碗大米饭9 分钟前
大厂一面,刨析题型,把握趋势🔭💯
前端·javascript·面试
星空寻流年15 分钟前
css3新特性第五章(web字体)
前端·css·css3
加油乐21 分钟前
JS计算两个地理坐标点之间的距离(支持米与公里/千米)
前端·javascript
小小小小宇21 分钟前
前端在 WebView 和 H5 环境下的缓存问题
前端
懒羊羊我小弟25 分钟前
React JSX 语法深度解析与最佳实践
前端·react.js·前端框架
冷冷清清中的风风火火28 分钟前
关于敏感文件或备份 安全配置错误 禁止通过 URL 访问 Vue 项目打包后的 .gz 压缩文件
前端·vue.js·安全