Js中函数式编程的理解 (2)

无状态和数据不可变Statelessness and Immutable data,是函数式编程的核心概念,为了实现这个目标,函数式编程提出函数应该具备的特性,没有副作用和纯函数。

数据不可变: 它要求你所有的数据都是不可变的,这意味着如果你想修改一个对象,那你应该创建一个新的对象用来修改,而不是修改已有的对象。

无状态: 主要是强调对于一个函数,不管你何时运行,它都应该像第一次运行一样,给定相同的输入,给出相同的输出,完全不依赖外部状态的变化。

没有副作用

没有副作用No Side Effects,是指在完成函数主要功能之外完成的其他副要功能,在我们函数中最主要的功能当然是根据输入返回结果,而在函数中我们最常见的副作用就是随意操纵外部变量。由于Js中对象传递的是引用地址,哪怕我们用const关键词声明对象,它依旧是可以变的。保证函数没有副作用,一来能保证数据的不可变性,二来能避免很多因为共享状态带来的问题。当你一个人维护代码时候可能还不明显,但随着项目的迭代,项目参与人数增加,大家对同一变量的依赖和引用越来越多,这种问题会越来越严重,最终可能连维护者自己都不清楚变量到底是在哪里被改变而产生Bug。传递引用一时爽,代码重构火葬场。

纯函数

纯函数pure functions,纯函数算是在没有副作用的要求上再进一步了。在Redux的三大原则中,我们看到它要求所有的修改必须使用纯函数,纯函数才是真正意义上的函数,它意味着相同的输入,永远会得到相同的输出,其实纯函数的概念很简单就是两点:

不依赖外部状态(无状态):函数的的运行结果不依赖全局变量,this指针、IO操作等。

没有副作用(数据不变):不修改全局变量,不修改入参。

流水线的构建

如果说函数式编程中有两种操作是必不可少的那无疑就是柯里化Currying和函数组合Compose,柯里化其实就是流水线上的加工站,函数组合就是我们的流水线,它由多个加工站组成。

柯里化

对于柯里化Currying,简单来说就是将一个多元函数,转换成一个依次调用的单元函数,也就是把一个多参数的函数转化为单参数函数的方法,函数的柯里化是用于将一个操作分成多步进行,并且可以改变函数的行为,在我的理解中柯里化实际就是实现了一个状态机,当达到指定参数时就从继续接收参数的状态转换到执行函数的状态。
简单来说,通过柯里化可以把函数调用的形式改变。

f(a,b,c) → f(a)(b)(c)

与柯里化非常相似的概念有部分函数应用Partial Function Application,这两者不是相同的,部分函数应用强调的是固定一定的参数,返回一个更小元的函数。

// 柯里化 f(a,b,c) → f(a)(b)(c) // 部分函数调用 f(a,b,c) → f(a)(b,c) / f(a,b)(c)

柯里化强调的是生成单元函数,部分函数应用的强调的固定任意元参数,而我们平时生活中常用的其实是部分函数应用,这样的好处是可以固定参数,降低函数通用性,提高函数的适合用性,在很多库函数中curry函数都做了很多优化,已经不是纯粹的柯里化函数了,可以将其称作高级柯里化,这些版本实现可以根据你输入的参数个数,返回一个柯里化函数/结果值,即如果你给的参数个数满足了函数条件,则返回值。
实现一个简单的柯里化的函数,可以通过闭包来实现。

var add = function(x) { return function(y) { return x + y; }; }; console.log(add(1)(2)); // 3

当有多个参数时,这样显然不够优雅,于是封装一个将普通函数转变为柯里化函数的函数。

function convertToCurry(funct, ...args) { const argsLength = funct.length; return function(..._args) { _args.unshift(...args); if (_args.length < argsLength) return convertToCurry.call(this, funct, ..._args); return funct.apply(this, _args); } } var funct = (x, y, z) => x + y + z; var addCurry = convertToCurry(funct); var result = addCurry(1)(2)(3); console.log(result); // 6

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zydzsp.html