JavaScript中的函数式编程详解


Posted in Javascript onAugust 22, 2020

函数式编程

函数式编程是一种编程范式,是一种构建计算机程序结构和元素的风格,它把计算看作是对数学函数的评估,避免了状态的变化和数据的可变,与函数式编程相对的是命令式编程。我们有这样一个需求,给数组的每个数字加一:

// 数组每个数字加一, 命令式编程
let arr = [1, 2, 3, 4];
let newArr = [];
for(let i = 0; i < arr.length; i++){
 newArr.push(arr[i] + 1);
}

console.log(newArr); // [2, 3, 4, 5]

这段代码结果没有问题,但是没法重用。我们换一个思维,这里面包含的操作其实就两个,一个是遍历数组,一个是成员加一。我们把这两个方法拆出来:

// 先拆加一出来
let add1 = x => x +1;

// 然后拆遍历方法出来,通过遍历返回一个操作后的新数组
// fn是我们需要对每个数组想进行的操作
let createArr = (arr, fn) => {
 const newArr = [];
 for(let i = 0; i < arr.length; i++){
 newArr.push(fn(arr[i]));
 }

 return newArr;
} 

// 用这两个方法来得到我们期望的结果
const arr = [1, 2, 3, 4];
const newArr = createArr(arr, add1);
console.log(newArr); // [2, 3, 4, 5], 结果仍然是对的

这样拆分后,如果我们下次的需求是对数组每个元素乘以2,我们只需要写一个乘法的方法,然后复用之前的代码就行:

let multiply2 = x => x * 2;

// 调用之前的createArr
const arr2 = [1, 2, 3, 4];
const newArr2 = createArr(arr2, multiply2);
console.log(newArr2); // [2, 4, 6, 8], 结果是对的

事实上我们的加一函数只能加一,也不好复用,它还可以继续拆:

// 先写一个通用加法,他接收第一个加数,返回一个方法
// 返回的这个方法接收第二个加数,第一个加数是上层方法的a
// 这样当我们需要计算1+2是,就是add(1)(2)
let add = (a) => {
 return (b) => {
 return a + b;
 }
}

// 我们也可以将返回的函数赋给一个变量,这个变量也就变成一个能特定加a的一个方法
let add1 = add(1);

let res = add1(4); 
console.log(res); // 5

所以函数式编程就是将程序分解为一些更可重用、更可靠且更易于理解的部分,然后将他们组合起来,形成一个更易推理的程序整体。

纯函数

纯函数是指一个函数,如果它的调用参数相同,则永远返回相同的结果。它不依赖于程序执行期间函数外部任何状态或数据的变化,只依赖于其输入参数。同时函数的运行也不改变任何外部数据,它只通过它的返回值与外部通讯。下面这个函数就不是纯函数,因为函数内部需要的discount需要从外部获取:

let discount = 0.8;
const calPrice = price => price * discount;
let price = calPrice(200); // 160

// 当discount变了,calPrice传同样额参数,结果不一样,所以不纯
discount = 0.9;
price = calPrice(200); // 180

要改为纯函数也很简单,将discount作为参数传递进去就行了

const calPrice = (price, discount) => price * discount;

纯函数可以保证代码的稳定性,因为相同的输入永远会得到相同结果。不纯的函数可能会带来副作用。

函数副作用

函数副作用是指调用函数时除了返回函数值之外,还对主调用函数产生附加的影响,比如修改全局变量或者外部变量,或者修改参数。这可能会带来难以查找的问题并降低代码的可读性。下面的foo就有副作用,当后面有其他地方需要使用a,可能就会拿到一个被污染的值

let a = 5;
let foo = () => a = a * 10;
foo();
console.log(a); // 50

除了我们自己写的函数有副作用外,一些原生API也可能有副作用,我们写代码时应该注意:

JavaScript中的函数式编程详解

我们的目标是尽可能的减少副作用,将函数写为纯函数,下面这个不纯的函数使用了new Date,每次运行结果不一样,是不纯的:

JavaScript中的函数式编程详解

要给为纯函数可以将依赖注入进去,所谓依赖注入就是将不纯的部分提取出来作为参数,这样我们可以让副作用代码集中在外部,远离核心代码,保证核心代码的稳定性

// 依赖注入
const foo = (d, log, something) => {
 const dt = d.toISOString();
 return log(`${dt}: ${something}`);
}

const something = 'log content';
const d = new Date();
const log = console.log.bind(console);
foo(d, log, something);

所以减少副作用一般的方法就是:

1. 函数使用参数进行运算,不要修改参数
2. 函数内部不修改外部变量
3. 运算结果通过返回值返回给外部

可变性和不可变性

  • 可变性:指一个变量创建以后可以任意修改
  • 不可变性: 指一个变量被创建后永远不会发生改变,不可变性是函数式编程的核心概念

下面是一个可变的例子:

JavaScript中的函数式编程详解

如果我们一定要修改这个参数,我们应该将这个参数进行深拷贝后再操作,这样就不会修改参数了:

JavaScript中的函数式编程详解

到此这篇关于JavaScript中的函数式编程详解的文章就介绍到这了,更多相关js函数式编程内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
Fastest way to build an HTML string(拼装html字符串的最快方法)
Aug 20 Javascript
js 数组操作之pop,push,unshift,splice,shift
Jan 29 Javascript
jQuery中insertBefore()方法用法实例
Jan 08 Javascript
Node.js中的流(Stream)介绍
Mar 30 Javascript
js数组依据下标删除元素
Apr 14 Javascript
javascript引用类型之时间Date和数组Array
Aug 27 Javascript
javaScript数组迭代方法详解
Apr 14 Javascript
JavaScript 中有关数组对象的方法(详解)
Aug 15 Javascript
Angular 中 select指令用法详解
Sep 29 Javascript
jquery配合.NET实现点击指定绑定数据并且能够一键下载
Oct 28 Javascript
原生js实现3D轮播图
Mar 21 Javascript
javascript中导出与导入实现模块化管理教程
Dec 03 Javascript
微信小程序中data-key属性之数据传输(经验总结)
Aug 22 #Javascript
Vue 电商后台管理项目阶段性总结(推荐)
Aug 22 #Javascript
vue实现移动端H5数字键盘组件使用详解
Aug 25 #Javascript
探究一道价值25k的蚂蚁金服异步串行面试题
Aug 21 #Javascript
js实现页面导航层级指示效果
Aug 25 #Javascript
js实现拖拽元素选择和删除
Aug 25 #Javascript
基于vue实现简易打地鼠游戏
Aug 21 #Javascript
You might like
[原创]PHP实现字节数Byte转换为KB、MB、GB、TB的方法
2017/08/31 PHP
javascript客户端解决方案 缓存提供程序
2010/07/14 Javascript
如何使用Jquery获取Form表单中被选中的radio值
2013/08/09 Javascript
jquery向上向下取整适合分页查询
2014/09/06 Javascript
ECMAScript 5严格模式(Strict Mode)介绍
2015/03/02 Javascript
黑帽seo劫持程序,js劫持搜索引擎代码
2015/09/15 Javascript
javascript编程异常处理实例小结
2015/11/30 Javascript
JavaScript简单实现鼠标移动切换图片的方法
2016/02/23 Javascript
jQuery解决input元素的blur事件和其他非表单元素的click事件冲突问题
2016/08/15 Javascript
Vue.js动态添加、删除选题的实例代码
2016/09/30 Javascript
JavaScript中全选、全不选、反选、无刷新删除、批量删除、即点即改入库(在yii框架中操作)的代码分享
2016/11/01 Javascript
Node.js pipe实现源码解析
2017/08/12 Javascript
利用React Router4实现的服务端直出渲染(SSR)
2019/01/07 Javascript
vue组件之间的数据传递方法详解
2019/04/19 Javascript
Element Collapse 折叠面板的使用方法
2020/07/26 Javascript
[01:02:03]2014 DOTA2华西杯精英邀请赛 5 24 NewBee VS VG
2014/05/26 DOTA
恢复百度云盘本地误删的文件脚本(简单方法)
2017/10/21 Python
python+opencv轮廓检测代码解析
2018/01/05 Python
对numpy中shape的深入理解
2018/06/15 Python
python 文件转成16进制数组的实例
2018/07/09 Python
python Django的web开发实例(入门)
2019/07/31 Python
Python高级特性——详解多维数组切片(Slice)
2019/11/26 Python
Win10下安装并使用tensorflow-gpu1.8.0+python3.6全过程分析(显卡MX250+CUDA9.0+cudnn)
2020/02/17 Python
最新2019Pycharm安装教程 亲测
2020/02/28 Python
如何用 Python 处理不平衡数据集
2021/01/04 Python
AmazeUI 列表的实现示例
2020/08/17 HTML / CSS
英国No.1体育用品零售商:SportsDirect.com
2019/10/16 全球购物
土地转让协议书
2014/09/27 职场文书
2014年安全生产工作总结
2014/11/13 职场文书
2014年社区妇联工作总结
2014/12/02 职场文书
罗马假日观后感
2015/06/08 职场文书
2016新年年会主持词
2015/07/06 职场文书
法制工作总结2015
2015/07/23 职场文书
六种css3实现的边框过渡效果
2021/04/22 HTML / CSS
Vue3中的Refs和Ref详情
2021/11/11 Vue.js
Java实现学生管理系统(IO版)
2022/02/24 Java/Android