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 相关文章推荐
javascript smipleChart 简单图标类
Jan 12 Javascript
js window.open弹出新的网页窗口
Jan 16 Javascript
深入解读JavaScript中的Iterator和for-of循环
Jul 28 Javascript
基于JQuery打造无缝滚动新闻步骤详解
Mar 31 Javascript
JavaScript简单获取页面图片原始尺寸的方法
Jun 21 Javascript
node.js平台下利用cookie实现记住密码登陆(Express+Ejs+Mysql)
Apr 26 Javascript
微信小程序获取微信运动步数的实例代码
Jul 20 Javascript
VueJs 搭建Axios接口请求工具
Nov 20 Javascript
bootstrap响应式工具使用详解
Nov 29 Javascript
vue3.0 CLI - 2.4 - 新组件 Forms.vue 中学习表单
Sep 14 Javascript
详解element-ui设置下拉选择切换必填和非必填
Jun 17 Javascript
vue+elementUI实现图片上传功能
Aug 20 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+js实现图片的上传、裁剪、预览、提交示例
2013/08/27 PHP
php命名空间学习详解
2014/02/27 PHP
php中动态变量用法实例
2015/06/10 PHP
PHP使用flock实现文件加锁的方法
2015/07/01 PHP
php获取指定(访客)IP所有信息(地址、邮政编码、国家、经纬度等)的方法
2015/07/06 PHP
PHP函数shuffle()取数组若干个随机元素的方法分析
2016/04/02 PHP
php解析xml 的四种简单方法(附实例)
2016/07/11 PHP
thinkPHP5.0框架验证码调用及点击图片刷新简单实现方法
2018/09/07 PHP
详解Laravel设置多态关系模型别名的方式
2019/10/17 PHP
在页面上点击任一链接时触发一个事件的代码
2007/04/07 Javascript
跨浏览器开发经验总结(三)   警惕“IE依赖综合症”
2010/05/13 Javascript
正则表达式搭配js轻松处理json文本方便而老古
2013/02/17 Javascript
Web跨浏览器进程通信(Web跨域)
2013/04/17 Javascript
js实现跟随鼠标移动且带关闭功能的图片广告实例
2015/02/26 Javascript
JS动态显示表格上下frame的方法
2015/03/31 Javascript
JavaScript中日期的相关操作方法总结
2015/10/24 Javascript
jquery实现下拉框功能效果【实例代码】
2016/05/06 Javascript
原生js实现回复评论功能
2017/01/18 Javascript
ES6新特性之解构、参数、模块和记号用法示例
2017/04/01 Javascript
Angular实现预加载延迟模块的示例
2017/10/12 Javascript
js中getBoundingClientRect的作用及兼容方案详解
2018/02/01 Javascript
vue路由教程之静态路由
2019/09/03 Javascript
如何阻止小程序遮罩层下方图层滚动
2019/09/05 Javascript
JavaScript数值类型知识汇总
2019/11/17 Javascript
element-ui中按需引入的实现
2019/12/25 Javascript
Python函数学习笔记
2008/10/07 Python
python爬虫URL重试机制的实现方法(python2.7以及python3.5)
2018/12/18 Python
使用Python的networkx绘制精美网络图教程
2019/11/21 Python
python3下pygame如何实现显示中文
2020/01/11 Python
Pytorch损失函数nn.NLLLoss2d()用法说明
2020/07/07 Python
面向对象编程OOP的优点
2013/01/22 面试题
毕业生自我鉴定范文
2013/11/08 职场文书
代理商会议邀请函
2014/01/27 职场文书
道路交通事故赔偿协议书
2014/10/24 职场文书
2016大学生社会实践单位评语
2015/12/01 职场文书
2016年企业安全生产月活动总结
2016/04/06 职场文书