JS函数式编程之纯函数、柯里化以及组合函数

目录
  • 前言
  • 纯函数
    • 纯函数的概念
    • 副作用
    • 纯函数案例
  • 柯里化
    • 柯里化的概念
  • 函数柯里化的过程
    • 函数柯里化的特点及应用
    • 自动柯里化函数的实现
  • 组合函数

前言

函数式编程(Functional Programming),又称为泛函编程,是一种编程范式。

早在很久以前就提出了函数式编程这个概念了,而后面一直长期被面向对象编程所统治着,最近几年函数式编程又回到了大家的视野中,JavaScript是一门以函数为第一公民的语言,必定是支持这一种编程范式的。

下面就来谈谈JavaScript函数式编程中的核心概念纯函数、柯里化以及组合函数。

纯函数

纯函数的概念

对于纯函数的定义,维基百科中是这样描述的:在程序设计中,若函数符合以下条件,那么这个函数被称之为纯函数。

  • 此函数在相同的输入值时,需产生相同的输出
  • 函数的输入和输出值以外的其他隐藏信息或状态无关,也和由I/O设备产生的外部输出无关
  • 该函数不能有语义上可观察的函数副作用,诸如“触发事件”,使输出设备输出,或更改输出值以外物件的内容等;

对以上描述总结就是:

  • 对于相同的输入,永远会得到相同的输出;
  • 在函数的执行过程中,没有任何可观察的副作用;
  • 同时也不依赖外部环境的状态;

副作用

上面提到了一个词叫“副作用”,那么什么是副作用呢?

  • 通常我们所说的副作用大多数是指药会产生的副作用;
  • 而在计算机科学中,副作用指在执行一个函数时,除了得到函数的返回值以外,还在函数调用时产生了附加的影响,比如修改了全局变量的状态,修改了传入的参数或得到了其它的输出内容等;

纯函数案例

  • 编写一个求和的函数sum,只要我们输入了固定的值,sum函数就会给我们返回固定的结果,且不会产生任何副作用。
function sum(a, b) {
 return a + b
}

const res = sum(10, 20)
console.log(res) // 30
  • 以下的sum函数虽然对于固定的输入也会返回固定的输出,但是函数内部修改了全局变量message,就认定为产生了副作用,不属于纯函数。
let message = 'hello'
function sum(a, b) {
 message = 'hi'
 return a + b
}
  • 在JavaScript中也提供了许多的内置方法,有些是纯函数,有些则不是。像操作数组的两个方法slice和splice。

1.slice方法就是一个纯函数,因为对于同一个数组固定的输入可以得到固定的输出,且没有任何副作用;

const nums = [1, 2, 3, 4, 5]
const newNums = nums.slice(1, 3)
console.log(newNums) // [2, 3]
console.log(nums) // [ 1, 2, 3, 4, 5 ]

2.splice方法不是一个纯函数,因为它改变了原数组nums;

const nums = [1, 2, 3, 4, 5]
const newNums = nums.splice(1, 3)
console.log(newNums) // [ 2, 3, 4 ]
console.log(nums) // [ 1, 5 ]

柯里化

柯里化的概念

对于柯里化的定义,维基百科中是这样解释的:

  • 柯里化是指把接收多个参数的函数,变成接收一个单一参数(最初函数的第一个参数)的函数,并且返回接收余下的参数,而且返回结果的新函数的技术;
  • 柯里化声称**“如果你固定某些参数,你将得到接受余下参数的一个函数”**;

总结:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩余的参数的过程就称之为柯里化。

函数柯里化的过程

编写一个普通的三值求和函数:

function sum(x, y, z) {
 return x + y + z
}
const res = sum(10, 20, 30)
console.log(res) // 60

将以上求和函数柯里化得:

  • 将传入的三个参数进行拆解,依次返回一个函数,并传入一个参数;
  • 在保证同样功能的同时,其调用方式却发生了变化;
  • 注意:在拆解参数时,不一定非要将参数拆成一个个的,也可以拆成2+1或1+2;
function sum(x) {
 return function(y) {
 return function(z) {
 return x + y + z
 }
 }
}
const res = sum(10)(20)(30)
console.log(res)

使用ES6箭头函数简写为:

const sum = x => y => z => x + y + z

函数柯里化的特点及应用

  • **让函数的职责更加单一。**柯里化可以实现让一个函数处理的问题尽可能的单一,而不是将一大堆逻辑交给一个函数来处理。

1.将上面的三值求和函数增加一个需求,在计算结果之前给每个值加上2,先看看不使用柯里化的实现效果:

function sum(x, y, z) {
 x = x + 2
 y = y + 2
 z = z + 2
 return x + y + z
}

2.柯里化的实现效果:

function sum(x) {
 x = x + 2
 return function(y) {
 y = y + 2
 return function(z) {
 z = z + 2
 return x + y + z
 }
 }
}

3.很明显函数柯里化后,让我们对每个参数的处理更加单一

  • **提高函数参数逻辑复用。**同样使用上面的求和函数,增加另一个需求,固定第一个参数的值为10,直接看柯里化的实现效果吧,后续函数调用时第一个参数值都为10的话,就可以直接调用sum10函数了。
function sum(x) {
 return function(y) {
 return function(z) {
 return x + y + z
 }
 }
}
const sum10 = sum(10) // 指定第一个参数值为10的函数
const res = sum10(20)(30)
console.log(res) // 60

自动柯里化函数的实现

function autoCurrying(fn) {
 // 1.拿到当前需要柯里化函数的参数个数
 const fnLen = fn.length

 // 2.定义一个柯里化之后的函数
 function curried\_1(...args1) {
 // 2.1拿到当前传入参数的个数
 const argsLen = args1.length

 // 2.1.将当前传入参数个数和fn需要的参数个数进行比较
 if (argsLen >= fnLen) {
 // 如果当前传入的参数个数已经大于等于fn需要的参数个数
 // 直接执行fn,并在执行时绑定this,并将对应的参数数组传入
 return fn.apply(this, args1)
 } else {
 // 如果传入的参数不够,说明需要继续返回函数来接收参数
 function curried\_2(...args2) {
 // 将参数进行合并,递归调用curried\_1,直到参数达到fn需要的参数个数
 return curried\_1.apply(this, [...args1, ...args2])
 }

 // 返回继续接收参数函数
 return curried\_2
 }
 }

 // 3.将柯里化的函数返回
 return curried\_1
}

测试:

function sum(x, y, z) {
 return x + y + z
}

const curryingSum = autoCurrying(sum)

const res1 = curryingSum(10)(20)(30)
const res2 = curryingSum(10, 20)(30)
const res3 = curryingSum(10)(20, 30)
const res4 = curryingSum(10, 20, 30)
console.log(res1) // 60
console.log(res2) // 60
console.log(res3) // 60
console.log(res4) // 60

组合函数

**组合函数(Compose Function)**是在JavaScript开发过程中一种对函数的使用技巧、模式。对某一个数据进行函数调用,执行两个函数,这两个函数需要依次执行,所以需要将这两个函数组合起来,自动依次调用,而这个过程就叫做函数的组合,组合形成的函数就叫做组合函数。

需求:对一个数字先进行乘法运算,再进行平方运算。

  • 一般情况下,需要先定义两个函数,然后再对其依次调用:
function double(num) {
 return num * 2
}
function square(num) {
 return num ** 2
}
const duobleResult = double(10)
const squareResult = square(duobleResult)
console.log(squareResult) // 400

实现一个组合函数,将duoble和square两个函数组合起来:

function composeFn(fn1, fn2) {
 return function(num) {
 return fn2(fn1(num))
 }
}
const execFn = composeFn(double, square)
const res = execFn(10)
console.log(res) // 400

实现一个自动组合函数的函数:

function autoComposeFn(...fns) {
 // 1.拿到需要组合的函数个数
 const fnsLen = fns.length

 // 2.对传入的函数进行边界判断,所有参数必须为函数
 for (let i = 0; i < fnsLen; i++) {
 if (typeof fns[i] !== 'function') {
 throw TypeError('The argument passed must be a function.')
 }
 }

 // 3.定义一个组合之后的函数
 function composeFn(...args) {
 // 3.1.拿到第一个函数的返回值
 let result = fns[0].apply(this, args)

 // 3.1.判断传入的函数个数
 if (fnsLen === 1) {
 // 如果传入的函数个数为一个,直接将结果返回
 return result
 } else {
 // 如果传入的函数个数 >= 2
 // 依次将函数取出进行调用,将上一个函数的返回值作为参数传给下一个函数
 // 从第二个函数开始遍历
 for (let i = 1; i < fnsLen; i++) {
 result = fns[i].call(this, result)
 }

 // 将结果返回
 return result
 }
 }

 // 4.将组合之后的函数返回
 return composeFn
}

测试:

function double(num) {
 return num * 2
}
function square(num) {
 return num ** 2
}

const composeFn = autoComposeFn(double, square)
const res = composeFn(10)
console.log(res) // 400

到此这篇关于JS函数式编程之纯函数、柯里化以及组合函数的文章就介绍到这了,更多相关纯函数、柯里化、组合函数内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • JavaScript函数式编程实现介绍

    目录 为什么要学习函数式编程 什么是函数式编程 前置知识 函数是一等公民 函数可以储存在变量中 函数作为参数 函数作为返回值 高阶函数 什么是高阶函数 使用高阶函数的意义 常用高阶函数 闭包 纯函数 纯函数概念 纯函数的好处 副作用 柯里化 函数组合 Functor(函子) MayBe 函子 Either函子 为什么要学习函数式编程 Vue进入3.*(One Piece 海贼王)世代后,引入的setup语法,颇有向老大哥React看齐的意思,说不定前端以后还真是一个框架的天下.话归正传,框架的趋

  • js面向对象编程OOP及函数式编程FP区别

    目录 写在前面 javscript 中函数和对象的关系 面向对象编程(OOP) 继承 多态 封装 函数编程编程(FP) 闭包和高阶函数 柯里化 偏函数 组合和管道 函子 写在最后 写在前面 浏览下文我觉得还是要有些基础的!下文涉及的知识点太多,基本上每一个拿出来都能写几篇文章,我在写文章的过程中只是做了简单的实现,我只是提供了一个思路,更多的细节还是需要自己去钻研的,文章内容也不少,辛苦,如果有其他的看法或者意见,欢迎指点,最后纸上得来终觉浅,绝知此事要躬行 javscript 中函数和对象的关

  • javascript函数式编程基础

    目录 一.引言 二.什么是函数式编程 三.纯函数(函数式编程的基石,无副作用的函数) 四.函数柯里化 五.函数组合 六.声明式和命令式代码 七.Point Free 八.示例应用 九.总结 一.引言 函数式编程的历史已经很悠久了,但是最近几年却频繁的出现在大众的视野,很多不支持函数式编程的语言也在积极加入闭包,匿名函数等非常典型的函数式编程特性.大量的前端框架也标榜自己使用了函数式编程的特性,好像一旦跟函数式编程沾边,就很高大上一样,而且还有一些专门针对函数式编程的框架和库,比如:RxJS.cy

  • JavaScript函数式编程(Functional Programming)组合函数(Composition)用法分析

    本文实例讲述了JavaScript函数式编程(Functional Programming)组合函数(Composition)用法.分享给大家供大家参考,具体如下: 组合(Composition)函数,就是把两个或以上的函数组合到一块儿,整成一个新的函数.我找到了一个很好的例子,很好地解释了组合函数这个概念. 比如一个应用主要是记录一下日常的花销(expenses),应用里的数据看起来像这样: const expenses = [ { name: '租金', price: 3000, type:

  • JavaScript中的函数式编程详解

    函数式编程 函数式编程是一种编程范式,是一种构建计算机程序结构和元素的风格,它把计算看作是对数学函数的评估,避免了状态的变化和数据的可变,与函数式编程相对的是命令式编程.我们有这样一个需求,给数组的每个数字加一: // 数组每个数字加一, 命令式编程 let arr = [1, 2, 3, 4]; let newArr = []; for(let i = 0; i < arr.length; i++){ newArr.push(arr[i] + 1); } console.log(newArr)

  • JS函数式编程之纯函数、柯里化以及组合函数

    目录 前言 纯函数 纯函数的概念 副作用 纯函数案例 柯里化 柯里化的概念 函数柯里化的过程 函数柯里化的特点及应用 自动柯里化函数的实现 组合函数 前言 函数式编程(Functional Programming),又称为泛函编程,是一种编程范式. 早在很久以前就提出了函数式编程这个概念了,而后面一直长期被面向对象编程所统治着,最近几年函数式编程又回到了大家的视野中,JavaScript是一门以函数为第一公民的语言,必定是支持这一种编程范式的. 下面就来谈谈JavaScript函数式编程中的核心

  • JavaScript函数柯里化

    目录 1 什么是函数柯里化 2 柯里化的作用和特点 2.1 参数复用 2.2 提前返回 2.3 延迟执行 3 封装通用柯里化工具函数# 4 总结和补充 1 什么是函数柯里化 在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术.这个技术以逻辑学家 Haskell Curry 命名的. 什么意思?简单来说,柯里化是一项技术,它用来改造多参数的函数. 比如: // 这是一个接受3个参数的函

  • 浅谈JS中的bind方法与函数柯里化

    绑定函数bind()最简单的用法是创建一个函数,使这个函数不论怎么调用都有同样的this值.不同于call和apply只是单纯地设置this的值后传参,它还会将所有传入bind()方法中的实参(第一个参数之后的参数)与this一起绑定. 关于这个特性看<JS权威指南>原文的例子: var sum = function(x,y) { return x + y }; var succ = sum.bind(null, 1); //让this指向null,其后的实参也会作为实参传入被绑定的函数sum

  • js函数柯里化的方法和作用实例分析

    本文实例讲述了js函数柯里化的方法和作用.分享给大家供大家参考,具体如下: 函数柯里化的方法: 1,利用数组的reduce方法可实现柯里化,具体参见我的另外一篇文章 2,代码如下 const curry = (fn, ...args) =>{ console.log('args',args) // console.log(fn.length,args.length) return args.length < fn.length // 参数长度不足时,重新柯里化该函数,等待接受新参数 ? (..

  • JavaScript函数柯里化实现原理及过程

    简介 当我们在读Vue源码到时候会发现,在它的 _update 实例中就用到了函数柯里化,(createPatchFunction方法)有兴趣的可以 去看一下. 柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术. 在<Mostly adequate guide>中,这样总结了 Currying --只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数. Currying 是函数式编

  • JavaScript函数柯里化原理与用法分析

    本文实例讲述了JavaScript函数柯里化原理与用法.分享给大家供大家参考,具体如下: 柯里化是这样的一个转换过程,把接受多个参数的函数变换成接受一个单一参数(译注:最初函数的第一个参数)的函数,如果其他的参数是必要的,返回接受余下的参数且返回结果的新函数. 也就是说是固定部分参数,返回一个接受剩余参数的函数,也称为部分计算函数,目的是为了缩小适用范围,创建一个针对性更强的函数. 例如,我想创建一个做自我介绍的函数,每个人只要输入自己姓名.性别.年龄即可.但是当A使用这个函数时,每次调用,都必

  • JavaScript函数柯里化详解

    什么是柯里化 柯里化是这样的一个转换过程,把接受多个参数的函数变换成接受一个单一参数(译注:最初函数的第一个参数)的函数,如果其他的参数是必要的,返回接受余下的参数且返回结果的新函数. 柯理化函数思想:一个js预先处理的思想:利用函数执行可以形成一个不销毁的作用域的原理,把需要预先处理的内容都储存在这个不销毁的作用域中,并且返回一个小函数,以后我们执行的都是小函数,在小函数中把之前预先存储的值进行相关的操作处理即可: 柯里化函数主要起到预处理的作用: bind方法的作用:把传递进来的callba

  • javascript实现函数柯里化与反柯里化过程解析

    函数柯里化(黑人问号脸)???Currying(黑人问号脸)???妥妥的中式翻译既视感:下面来一起看看究竟什么是函数柯里化: 维基百科的解释是:把接收多个参数的函数变换成接收一个单一参数(最初函数的第一个参数)的函数,并返回接受剩余的参数而且返回结果的新函数的技术.其由数学家Haskell Brooks Curry提出,并以curry命名. 概念往往都是干涩且难懂的,让我们用人话来解释就是:如果我们不确定这个函数有多少个参数,我们可以先给它传入一个参数,然后通过JS闭包(如若不懂JS闭包,请先学

  • 详解用场景去理解函数柯里化(入门篇)

    前言 函数柯里化就是将多参简化为单参数的一种技术方式,其最终支持的是方法的连续调用,每次返回新的函数,在最终符合条件或者使用完所有的传参时终止函数调用. 场景实例 与其他文章不同,我在本文会重点分享一些柯里化的经典使用场景,让你在学会这点技巧后能切实的提升代码的可维护性. 编写可重用小模块代码 比如我们有个方法部分逻辑前置是相同的,后面的执行是因为参数不同导致结果不同的,下面是代码部分. 计算商品的折扣,我们需要根据不同的折扣以及商品的入参返回其实际的价格. // before function

  • 怎样用Javascript实现函数柯里化与反柯里化

    函数柯里化(黑人问号脸)???Currying(黑人问号脸)???妥妥的中式翻译既视感:下面来一起看看究竟什么是函数柯里化: 维基百科的解释是:把接收多个参数的函数变换成接收一个单一参数(最初函数的第一个参数)的函数,并返回接受剩余的参数而且返回结果的新函数的技术.其由数学家Haskell Brooks Curry提出,并以curry命名. 概念往往都是干涩且难懂的,让我们用人话来解释就是:如果我们不确定这个函数有多少个参数,我们可以先给它传入一个参数,然后通过JS闭包(如若不懂JS闭包,请先学

随机推荐