广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >javascript函数式编程基础
  • 822
分享到

javascript函数式编程基础

2024-04-02 19:04:59 822人浏览 泡泡鱼
摘要

目录一、引言二、什么是函数式编程三、纯函数(函数式编程的基石,无副作用的函数)四、函数柯里化五、函数组合六、声明式和命令式代码七、Point Free八、示例应用九、总结 一、引言

一、引言

函数式编程的历史已经很悠久了,但是最近几年却频繁的出现在大众的视野,很多不支持函数式编程的语言也在积极加入闭包,匿名函数等非常典型的函数式编程特性。大量的前端框架也标榜自己使用了函数式编程的特性,好像一旦跟函数式编程沾边,就很高大上一样,而且还有一些专门针对函数式编程的框架和库,比如:Rxjs、cycleJS、ramdaJS、lodashJS、underscoreJS 等。函数式编程变得越来越流行,掌握这种编程范式对书写高质量和易于维护的代码都大有好处,所以我们有必要掌握它。

二、什么是函数式编程

维基百科定义:函数式编程(英语:functional programming),又称泛函编程,是一种编程范式,它将电脑运算视为数学上的函数计算,并且避免使用程序状态以及易变对象。

三、纯函数(函数式编程的基石,无副作用的函数)

在初中数学里,函数 f 的定义是:对于输入 x 产生一个唯一输出 y=f(x)。这便是纯函数。它符合两个条件:1.此函数在相同的输入值时,总是产生相同的输出。函数的输出和当前运行环境的上下文状态无关。2.此函数运行过程不影响运行环境,也就是无副作用(如触发事件、发起 Http 请求、打印/log 等)。简单来说,也就是当一个函数的输出不受外部环境影响,同时也不影响外部环境时,该函数就是纯函数,也就是它只关注逻辑运算和数学运算,同一个输入总得到同一个输出。javascript 内置函数有不少纯函数,也有不少非纯函数。

纯函数:Array.prototype.sliceArray.prototype.mapString.prototype.toUpperCase

非纯函数:Math.randomDate.nowArray.ptototype.splice

这里我们以 slice 和 splice 方法举例:


var xs = [1,2,3,4,5];
// 纯的
xs.slice(0,3);
//=> [1,2,3]
xs.slice(0,3);
//=> [1,2,3]
xs.slice(0,3);
//=> [1,2,3]

// 不纯的
xs.splice(0,3);
//=> [1,2,3]
xs.splice(0,3);
//=> [4,5]
xs.splice(0,3);
//=> []


我们看到调用数组的 slice 方法每次返回的结果完全相同,同时 xs 不会被改变,而调用 splice 方法每次返回值都不一样,同时 xs 变得面目全非。这就是我们强调使用纯函数的原因,因为纯函数相对于非纯函数来说,在可缓存性、可移植性、可测试性以及并行计算方面都有着巨大的优势。这里我们以可缓存性举例:


var squareNumber  = memoize(function(x){ return x*x; });
squareNumber(4);
//=> 16
squareNumber(4); // 从缓存中读取输入值为 4 的结果
//=> 16


那我们如何把一个非纯函数变纯呢?比如下面这个函数:


var minimum = 21;
var checkAge = function(age) {
  return age >= minimum;
};


这个函数的返回值依赖于可变变量 minimum 的值,它依赖于系统状态。在大型系统中,这种对于外部状态的依赖是造成系统复杂性大大提高的主要原因。


var checkAge = function(age) {
  var minimum = 21;
  return age >= minimum;
};


通过改造,我们把 checkAge 变成了一个纯函数,它不依赖于系统状态,但是 minimum 是通过硬编码的方式定义的,这限制了函数的扩展性,我们可以在后面的柯里化中看到如何优雅的使用函数式解决这个问题。所以把一个函数变纯的基本手段是不要依赖系统状态。

四、函数柯里化

curry 的概念很简单:将一个低阶函数转换为高阶函数的过程就叫柯里化。

用一个形象的比喻就是:

比如对于加法操作:var add = (x, y) => x + y,我们可以这样柯里化:


//es5写法
var add = function(x) {
  return function(y) {
    return x + y;
  };
};

//es6写法
var add = x => (y => x + y);

//试试看
var increment = add(1);
var addTen = add(10);

increment(2);  // 3

addTen(2);  // 12

对于加法这种极其简单的函数来说,柯里化并没有什么用。还记得上面的 checkAge 函数吗?我们可以这样柯里化它:


var checkage = min => (age => age > min);
var checkage18 = checkage(18);
checkage18(20);
// =>true


这表明函数柯里化是一种“预加载”函数的能力,通过传递一到两个参数调用函数,就能得到一个记住了这些参数的新函数。从某种意义上来讲,这是一种对参数的缓存,是一种非常高效的编写函数的方法:


var curry = require('lodash').curry;

//柯里化两个纯函数
var match = curry((what, str) => str.match(what));
var filter = curry((f, ary) => ary.filter(f));

//判断字符串里有没有空格
var hasSpaces = match(/\s+/g);

hasSpaces("hello world");  // [ ' ' ]
hasSpaces("spaceless");  // null

var findSpaces = filter(hasSpaces);

findSpaces(["tori_spelling", "tori amos"]);  // ["tori amos"]

五、函数组合

假设我们需要对一个字符串做一些列操作,如下,为了方便举例,我们只对一个字符串做两种操作,我们定义了一个新函数 shout,先调用 toUpperCase,然后把返回值传给 exclaim 函数,这样做有什么不好呢?不优雅,如果做得事情一多,嵌套的函数会非常深,而且代码是由内往外执行,不直观,我们希望代码从右往左执行,这个时候我们就得使用组合。


var toUpperCase = function(x) { return x.toUpperCase(); };
var exclaim = function(x) { return x + '!'; };

var shout = function(x){
  return exclaim(toUpperCase(x));
};

shout("send in the clowns");
//=> "SEND IN THE CLOWNS!"

使用组合,我们可以这样定义我们的 shout 函数:


//定义compose
var compose = (...args) => x => args.reduceRight((value, item) => item(value), x);

var toUpperCase = function(x) { return x.toUpperCase(); };
var exclaim = function(x) { return x + '!'; };

var shout = compose(exclaim, toUpperCase);

shout("send in the clowns");
//=> "SEND IN THE CLOWNS!"

代码从右往左执行,非常清晰明了,一目了然。我们定义的 compose 像 N 面胶一样,可以将任意多个纯函数结合到一起。这种灵活的组合可以让我们像拼积木一样来组合函数式的代码:


var head = function(x) { return x[0]; };
var reverse = reduce(function(acc, x){ return [x].concat(acc); }, []);
var last = compose(head, reverse);

last(['jumpkick', 'roundhouse', 'uppercut']);
//=> 'uppercut'

六、声明式和命令式代码

命令式代码:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。声明式代码:告诉“机器”你想要的是什么(what),让机器想出如何去做(how)。与命令式不同,声明式意味着我们要写表达式,而不是一步一步的指示。以 sql 为例,它就没有“先做这个,再做那个”的命令,有的只是一个指明我们想要从数据库取什么数据的表达式。至于如何取数据则是由它自己决定的。以后数据库升级也好,SQL 引擎优化也好,根本不需要更改查询语句。这是因为,有多种方式解析一个表达式并得到相同的结果。这里为了方便理解,我们来看一个例子:


// 命令式
var makes = [];
for (var i = 0; i < cars.length; i++) {
  makes.push(cars[i].make);
}

// 声明式
var makes = cars.map(function(car){ return car.make; });

命令式的循环要求你必须先实例化一个数组,而且执行完这个实例化语句之后,解释器才继续执行后面的代码。然后再直接迭代 cars 列表,手动增加计数器,就像你开了一辆零部件全部暴露在外的汽车一样。这不是优雅的程序员应该做的。声明式的写法是一个表达式,如何进行计数器迭代,返回的数组如何收集,这些细节都隐藏了起来。它指明的是做什么,而不是怎么做。除了更加清晰和简洁之外,map 函数还可以进一步独立优化,甚至用解释器内置的速度极快的 map 函数,这么一来我们主要的业务代码就无须改动了。函数式编程的一个明显的好处就是这种声明式的代码,对于无副作用的纯函数,我们完全可以不考虑函数内部是如何实现的,专注于编写业务代码。优化代码时,目光只需要集中在这些稳定坚固的函数内部即可。相反,不纯的不函数式的代码会产生副作用或者依赖外部系统环境,使用它们的时候总是要考虑这些不干净的副作用。在复杂的系统中,这对于程序员的心智来说是极大的负担。

七、Point Free

pointfree 模式指的是,永远不必说出你的数据。它的意思是说,函数无须提及将要操作的数据是什么样的。一等公民的函数、柯里化(curry)以及组合协作起来非常有助于实现这种模式。


// 非 pointfree,因为提到了数据:Word
var snakeCase = function (word) {
  return word.toLowerCase().replace(/\s+/ig, '_');
};

// pointfree
var snakeCase = compose(replace(/\s+/ig, '_'), toLowerCase);

这种风格能够帮助我们减少不必要的命名,让代码保持简洁和通用。当然,为了在一些函数中写出 Point Free 的风格,在代码的其它地方必然是不那么 Point Free 的,这个地方需要自己取舍。

八、示例应用

拥有了以上的知识,我们是时候该写一个示例应用了。这里我们使用了 ramda ,没有用 lodash 或者其他类库。ramda 提供了 compose、curry 等很多函数。我们的应用将做四件事:

1.根据特定搜索关键字构造 url

2.向 flickr 发送 api 请求

3.把返回的 JSON 转为 html 图片

4.把图片放到屏幕上上面提到了两个不纯的动作,即从 flickr 的 api 获取数据和在屏幕上放置图片这两件事。我们先来定义这两个动作,这样就能隔离它们了。这里我们只是简单包装了一下 Jquery 的 getJSON 函数,把它变为一个 curry 函数,还有就是把参数位置也调换了下,我们把它们放在 Impure 命名空间下以用来隔离,这样我们就知道它们都是危险函数。运用函数柯里化和函数组合的技巧,我们就可以创建一个函数式的实际应用了:

预览地址:https://code.h5jun.com/vixe/1/edit?html,js,output 看看,多么美妙的声明式规范啊,只说做什么,不说怎么做。现在我们可以把每一行代码都视作一个等式,变量名所代表的属性就是等式的含义。

九、总结

我们已经见识到如何在一个小而不失真实的应用中运用新技能了,但是异常处理以及代码分支呢?如何让整个应用都是函数式的,而不仅仅是把破坏性的函数放到命名空间下?如何让应用更安全更富有表现力?我会在下一篇文章中介绍函数式编程的更加高阶一些的知识,例如 Functor、Monad、Applicative 等概念。

以上就是javascript函数式编程基础的详细内容,更多关于javascript函数式编程的资料请关注编程网其它相关文章!

--结束END--

本文标题: javascript函数式编程基础

本文链接: https://www.lsjlt.com/news/135095.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • javascript函数式编程基础
    目录一、引言二、什么是函数式编程三、纯函数(函数式编程的基石,无副作用的函数)四、函数柯里化五、函数组合六、声明式和命令式代码七、Point Free八、示例应用九、总结 一、引言...
    99+
    2022-11-12
  • JavaScript基础之函数详解
    目录一、函数简介1.1.函数的创建1.2.函数的参数和返回值二、函数的类型2.1. 构造函数2.2. 立即执行函数2.3. 构造函数的原型prototype2.4. 函数中的this...
    99+
    2022-11-12
  • JavaScript基础函数有哪些
    本篇内容介绍了“JavaScript基础函数有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成! 一、时...
    99+
    2022-10-19
  • Python编程基础之函数和模块
    目录二、函数(一)定义函数1、语法格式2、函数类型3、案例演示(二)调用函数1、简要说明2、案例演示(三)函数参数1、参数的多态性2、参数赋值传递三、利用函数实现模块化1、创建多级菜...
    99+
    2022-11-12
  • Python 函数编程的基础知识介绍
    函数基础知识掌握自定义函数的基本语法规范和调用方法及掌握函数的各种参数的使用及调用规则。1、Python函数函数( Function )是组织好的,可重复使用的,用来实现单一, 或相关联功能的代码段。函数能提高应用的模块性 ,和代码的重复利...
    99+
    2023-05-14
    Python 函数编程 自定义函数
  • JavaScript基础的函数有哪些
    今天就跟大家聊聊有关JavaScript基础的函数有哪些,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。一、函数简介1.1.函数的创建函数创建的三种方式// 方式1:字符串封...
    99+
    2023-06-22
  • 函数基础
    目录 函数体系 什么是函数?(掌握) 为何用函数(掌握) 如何用函数(掌握) 注册功能函数 登录功能函...
    99+
    2023-01-31
    函数 基础
  • JavaScript基础之立即执行函数
    目录立即执行函数格式立即执行函数其他方式–表达式立即执行函数可以带参数应用总结在JavaScript中有时候看到一些很神奇的函数比如下面截图: 这种函数只要浏览器加载的时候会自动运...
    99+
    2022-11-12
  • Java8函数式接口的基础学习教程
    函数式接口 1.1 函数式接口概述 函数式接口:有且仅有一个抽象方法的接口 Java中的函数式编程体现就是Lambda表达式,所以函数式接口就是可以使用与Lambda使用的接口 只...
    99+
    2022-11-12
  • 用JavaScript实现函数式编程
    这篇文章主要讲解了“用JavaScript实现函数式编程”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“用JavaScript实现函数式编程”吧!函数式编程语...
    99+
    2022-10-19
  • 什么是JavaScript函数式编程
    这篇文章主要讲解了“什么是JavaScript函数式编程”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“什么是JavaScript函数式编程”吧!JavaSc...
    99+
    2022-10-19
  • javascript函数式编程的用法
    本篇内容介绍了“javascript函数式编程的用法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!说明函数式编程是一种编程范式,是一种软件开...
    99+
    2023-06-20
  • 《Java 基础篇》之函数式接口
    目录 1、函数式接口概述 2、函数式接口做为方法的参数 3、函数式接口作为方法的返回值 3、常用的函数式接口 3.1、Supplier [səˈplaɪə(r)] Supplier接口 3.2、Consumer接口 3.3、Predicat...
    99+
    2023-10-20
    java 开发语言
  • Python函数基础
    目录Python函数1、函数的定义格式2、使用当前文件的函数3、调用及定义函数4、使用其他文件(模块)的函数5、求两数之和Python函数 函数就是把具有独立功能的代码块封装成一个小...
    99+
    2022-11-12
  • python-函数基础
    函数简介 定义:就是具有特定功能的一段代码 优点: 解决代码的重复书写 可以将功能的实现着和使用者分开,提高开发效率 分类: 库函数:print、input、abs等 自定义:用户自己封装的函数 ...
    99+
    2023-01-30
    函数 基础 python
  • Python基础函数
    join()函数的用法   join()函数连接字符串数组。将字符串、元组、列表中的元素以指定的字符(分隔符)连接生成一个新的字符串 语法:'sep'.join(seq) 参数说明sep:分隔符。可以为空seq:要连接的元素序列、字符串、...
    99+
    2023-01-31
    函数 基础 Python
  • JavaScript编程基础知识有哪些
    这篇“JavaScript编程基础知识有哪些”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“...
    99+
    2022-10-19
  • JavaScript基础学习之splice()函数详解
    目录splice()函数详解一、情况一(只有一个参数)二、情况二 (两个参数)三、情况三 (大于等于三个参数)总结splice()函数详解 splice() 方法向/从数组中添加/删...
    99+
    2022-11-13
  • 从历史讲起JavaScript基因里的函数式编程实例
    目录本篇序言一、数学之美二、lambda 演算核心三、JavaScript 的基因本篇序言 本瓜很喜欢看历史,读史可知兴替、使人明智,作为程序员看“技术的演替历史&rdq...
    99+
    2022-11-13
    JavaScript基因函数式编程 JavaScript 函数式编程
  • MySQL基础教程12 —— 函数之其他函数
    1. 位函数 对于比特运算,MySQL 使用 BIGINT (64比特) 算法,因此这些操作符的最大范围是 64 比特。 | Bitwise OR: mysql> SELECT 29 | 15; -...
    99+
    2022-05-17
    MySQL 位函数 信息函数 函数 加密函数
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作