JavaScript与函数式编程

绝大多数编程语言都会有函数的概念(或者说所有的?我不太确定),他们都可以做出类似的操作:

function(x) {
return x * x
}

但是Javascript更适合函数式编程,因为函数对于js来说,是一等公民

我们可以把匿名函数赋值给一个变量,比如:

let pow = function(x) {
return x * x
}

然后我们可以将这个函数赋值给另一个变量:

let comeon = pow
comeon(x)

这样做和直接调用pow(x)是一样的效果。

甚至于我们可以将函数作为参数传入另一个函数,这样,诸多小函数可以汇聚在一起,变得异常强大!

filter()

OK,我们接下来看一些比较基础的例子。

首先是filter(),这是我最喜欢的函数之一。filter()方法会创建一个新的数组,并且我们可以传入一个判断函数,将符合条件的元素,放入新的数组。

现在我们有一个数组,里面存放了很多游戏,每个元素都记录了该游戏需要花多少钱:

let games = [
{
name: '英雄联盟',
cost: 45
},
{
name: '穿越火线',
cost: 888
},
{
name: '魔兽世界',
cost: 75
},
{
name: '征途',
cost: 1000000
}
]

然后我们需要找出,花费不超过一百元的游戏,该怎么做呢?

可能是这样:

let target = []
for(let i = 0; i < games.length; i++) {
if(games[i].cost <= 100) {
target.push(games[i])
}
}

这是大家从大学开始学C语言的时候就会用的方法。但是我现在想用filter()方法重写它:

let target = games.filter((game) => {
return game.cost <= 100
})

Wow! Awesome!

我稍微解释一下,防止有同学没有看懂,这里传入了一个函数,函数接收了一个参数,这个参数就是games的每一个元素依次传入的值,在每一次传入之后,我们都返回一个逻辑值,这个逻辑值取决于该游戏的cost是否小于100,如果返回了true该元素就会被放到新的数组里去,反之同理。

注意:

  • filter()不会对空数组进行检测
  • filter()不会改变原始数组

实际上,filter内部的处理方法可能和我们使用for循环一模一样!但是我们利用函数式编程,写了更少的代码、更少的逻辑。Less code! Less time! Less bug!

这就是函数式编程的美妙之处。

map()

我们现在先看回之前写的数组:

let games = [
{
name: '英雄联盟',
cost: 45
},
{
name: '穿越火线',
cost: 888
},
{
name: '魔兽世界',
cost: 75
},
{
name: '征途',
cost: 1000000
}
]

现在我们要把每一款游戏的名字都拿出来,组成一个新的数组。

这种操作是非常常见的,比如我们使用React,向服务器请求了JSON数据,接下来需要在这里渲染名字,那里渲染价格……等等。

let gameName = games.map((item) => {
return item.name
})

我们甚至可以做出更多的骚操作,比如说:

let gameName = games.map((item) => {
return `${item.name}是一个${item.cost > 100? '坑钱游戏':'良心游戏'}`
})

得到结果["英雄联盟是一个良心游戏", "穿越火线是一个坑钱游戏", "魔兽世界是一个良心游戏", "征途是一个坑钱游戏"]

Wow! Awesome! 只能用优雅两个字来形容!

reduce()

reduce()单词本身是减少的意思,但是实际上你可以将reduce()理解为求和(事实并不如此,reduce更加强大且灵活,但是此时可以暂时这么理解,更多特性可以在下一节看到)。

语言太过苍白无力,我们来看看代码:

let numbers = [1, 2, 3, 4, 5]
let sum = numbers.reduce((total, item) => total + item)

这里是利用了箭头函数可以省略return的特性。

这里的传入的函数接收了两个参数(实际上可以接收四个,但这里不需要后面两个),这两个参数通过英文应该就可以看懂。

reduce()方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。

注意: reduce()对于空数组是不会执行回调函数的。

这里可以给一个初始值:

let numbers = [1, 2, 3, 4, 5]
let sum = numbers.reduce((total, item) => {
return total + item
}, 10)

之前的和是15,这次加上了一个初始值,就是25了。

实践

假设我们有一个TXT文件。

徐航宇	游泳	4分钟
徐航宇 跑步 1分钟
徐航宇 跳远 3米
刘好 游泳 5分钟
刘好 跑步 2分钟
刘好 跳远 1米

我们用node去读取他,让它变成一个JSON对象,就像这样:

{
"徐航宇": [
{
"项目": "游泳",
"时间": "4分钟"
},
{
"项目":"跑步",
"时间":"1分钟"
}
],
"刘好": [
// ...
]
}

上代码!

import fs from 'fs'

let output = fs.readFileSync('data.txt', 'utf-8')
.trim() // 去掉字符串头尾的空格,返回新数组
.split('\n') // 在换行处截断,组成数组
.map(line => line.split('\t')) // 每一行根据制表符断开,组成数组

这一步之后,我们应该得到什么?

[
["徐航宇", "游泳", "4分钟"],
["徐航宇", "跑步", "1分钟"],
["徐航宇", "跳远", "3米"],
["刘好", "游泳", "5分钟"],
["刘好", "跑步", "2分钟"],
["刘好", "跳远", "1米"]
]

接下来想要变成对象,该怎么做呢?

首先第一步,我们想一想,要想变成最终的JSON数据,我们需要对每一项进行处理:

  • 把每一个数组的第一个作为对象的属性名
  • 把每一个数组的二三项组成新的对象,放入该属姓名的值中

接下来就该reduce()出场了:

.reduce((customer, line) => {
// 提取每个数组的第一项,作为传入对象的属姓名
// 如果该项是以前没有的,那么将其初始化为空数组
// 如果该项以前有,那么就不动他
customer[line[0]] = customer[line[0]] === undefined ? [] : customer[line[0]]
customer[line[0]].push({
name: line[1],
time: line[2]
})
return customer
}, {})

这样就成功了!

总结

总而言之,函数式编程如果归根结底,和直接写没有任何区别。但是它提供给了我们一种写更少的代码,完成更多的事情的方法。

人是难免会出错的,代码量越大、错误可能就会越多,所以更少代码的函数式编程,往往意味着:更少的bug。

(完)

JavaScript与函数式编程的更多相关文章

  1. 翻译连载 | 第 9 章:递归(下)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...

  2. 翻译连载 |《你不知道的JS》姊妹篇 |《JavaScript 轻量级函数式编程》- 引言&前言

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 译者团队(排名不分先后):阿希.blueken.brucec ...

  3. 翻译连载 | 第 10 章:异步的函数式(上)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...

  4. 翻译连载 | 第 10 章:异步的函数式(下)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...

  5. 翻译连载 | 第 11 章:融会贯通 -《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...

  6. 翻译连载 | 附录 A:Transducing(上)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...

  7. 翻译连载 | 附录 A:Transducing(下)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...

  8. 翻译连载 | 附录 B: 谦虚的 Monad-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...

  9. 翻译连载 | 附录 C:函数式编程函数库-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...

  10. 全本 | iKcamp翻译 | 《JavaScript 轻量级函数式编程》|《你不知道的JS》姊妹篇

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson - <You-Dont-Know-JS>作者 译者团队(排名不分先后):阿希.blueken.bruc ...

随机推荐

  1. Effective Java —— 使类和成员的可访问性最小化

    本文参考 本篇文章参考自<Effective Java>第三版第十五条"Minimize the accessibility of classes and members&quo ...

  2. can总线第三讲

    一  CAN控制器官方定义:CAN控制器用于将欲收发的信息(报文),转换为符合CAN规范的CAN帧,通过CAN收发器,在CAN-bus上交换信息.举个便于理解的例子:就像您发快递一样,要根据快递公司提 ...

  3. python学习笔记(六)——程序调试

    在我们平时编写程序时,常常会遇到各种错误,俗称BUG.而我们程序猿的工作常常需要对程序进行调试,也就是所谓的debug. 程序调试是将编制的程序投入实际运行前,用手工或编译程序等方法进行测试,修正语法 ...

  4. 几种常见布局的flex写法

    flex布局目前基本上兼容主流的浏览器,且实现方式简单.我整理了flex的一些知识点,并且总结归纳了几种常见布局的flex写法 ​flex基础知识点 flex-grow和flex-shrink相关计算 ...

  5. 无单位数字和行高 —— 别说你懂CSS相对单位

    前段时间试译了Keith J.Grant的CSS好书<CSS in Depth>,其中的第二章<Working with relative units>,书中对relative ...

  6. pydev+eclipse写python代码

    首先,下载pydev:PyDev for Eclipse - Browse /pydev at SourceForge.net (建议下载到本地,之前看其他文章时,进行了如下安装: 启动 Eclips ...

  7. JSDOM基础

    JavaScript 通过 HTML DOM,可访问 JavaScript HTML 文档的所有元素. HTML DOM 模型被构造为对象的树: HTML DOM 树 JavaScript 能够改变页 ...

  8. git生成和添加SSH公钥

    一 .前言: 大家换电脑.换公司的时候,经常要关联本地git和git线上仓库, 在这里我就顺便记一下,好记性不如烂笔头, 以后找起来来方便 二 .查看自己是否生成过公钥,有的话可以直接拿过来用, 也可 ...

  9. 2021-ACM-ICPC-济南站 K Search For Mafuyu 【树的遍历与回溯,dfs, 邻接表】

    PAT 题目详情 (pintia.cn) 题目 题意描述 n个房间, 有n-1个通道, kanade初始在1,Mafuyu 位置未知,求K到M的最小期望,也就是到每个地方的平均值,注意的是这里的求法, ...

  10. DOS攻击(一)

    DOS攻击(一) 介绍 DoS是Denial of Service的简称,即拒绝服务,造成DoS的攻击行为被称为DoS攻击,其目的是使计算机或网络无法提供正常的服务.最常见的DoS攻击有计算机网络带宽 ...