Open ccforward opened 7 years ago
很强!
搭车,安利一篇我的知乎回答:JavaScript 函数式编程存在性能问题么?
再次遇见@justjavac前辈大神😄
推荐lodash、ramda,数据结构转换超级方便,基本上不用自己写转换逻辑,理清思路找API就行了。
哇!惊现学长@rccoder
// 输入为state和originData变量
// 输出为:
{
houseTypeShared: '2居_3居',
houseTypeWhole: '2居',
};
// state和originData变量如下:
// state
const state = {
sharedRooms: { 0: false, 1: true, 2:true },
wholeRooms: { 2: false, 3: true },
};
// 源数据
const originData = {
sharedRooms: [
{
unique: true,
text: '不限',
value: 'UNLIMITED',
},
{
text: '2居',
value: 'TWO',
},
{
text: '3居',
value: 'THREE',
},
{
text: '3居+',
value: 'THREE_MORE',
},
],
wholeRooms: [
{
unique: true,
text: '不限',
value: 'UNLIMITED',
},
{
text: '1居',
value: 'ONE',
},
{
text: '2居',
value: 'TWO',
},
{
text: '2居+',
value: 'TWO_MORE',
},
]
};
// 利用map, reduce, filter等函数式方法,计算出结果。
/*
好处:
1.代码精简
2.map,reduce,filter等方法更具语义化
坏处:
1.初次阅读,难以阅读理解(其实写习惯,就很容易理解了)
*/
const TypeMapParamKey = {
sharedRooms: 'houseTypeShared',
wholeRooms: 'houseTypeWhole',
};
Object.keys(state)
.map(type => {
const rtText = Object.keys(state[type])
.filter(index => state[type][index])
.map(index => originData[type][index].text)
.reduce((rt, text, index) =>
(`${rt}` + (index === 0 ? '' : '_') + `${text}`),
'',
);
return {
[TypeMapParamKey[type]]: rtText,
};
})
.reduce((rt, item) => Object.assign(rt, item), {})
不用循环的 JavaScript -- 函数式编程
通过代码一步步的深入
map
reduce
filter
find
...循环 Loop
先看看js中的基础循环代码
while
使用
while
循环来大写数组中每个元素这种循环太常见了,一个初始值为0计数器
i
,每次循环需要递增i
的值,并通过len
来判断循环终止条件。简单的
for
循环代码如下:比起
while
for
循环把计数器i
放在循环顶部,避免了while
中忘记计数器自增自增而引起的死循环。但是后退一步看,我们只是要把每一项大写,但是并不在乎循环中的计数器
i
因为这种对数组每一项进行循环操作的模式很常见,所以有了 ES2015 中的
for..of
循环用了
for...of
省了计数器和循环终止的判断,也不需要抽取出数组每一项来处理映射 Mapping
虽然
for...of
更简洁,但还是要初始newCompany
并执行push
如何做到更加精简
如果现在有两个数组需要
capitalize
呢两次循环 简单粗暴:
这看起来不是很 DRY (Don't repeat yourself) ,所以可以重构下
这样看起来还行,但是如果新加一个功能,只是首字母大写呢
如果这样写,下面的代码依然不是很 DRY
那就继续重构,抽象成给定一个数组和一个函数,然后将数组中每一项映射到新的数组的模式
但代码里依然有个循环,只能写成递归的形式
递归的代码极简,但在旧浏览器里有性能问题,所以我们就直接用 js 内置的
map
好了同样简单粗暴
像函数式编程一样,分而治之,两个用来做大写字符串的函数只关注本身的功能,不关心数据从哪来;同时
map
只负责传递函数,不关心函数是干什么的。Reduce
map
在输出和输入的相同长度的数组上很好用。但是如果再添加一个数字数组或者在一个 list 中找出最短的字符串呢。
如下,有个 worker 的数组,包含姓名和薪水
找出 salary 最高的那位
上面的代码那样写也没什么问题。为了深入一点,我们现在需要所有人 salary 的总和,代码如下
上面两个例子中都需要在循环之前定义一个变量,然后在每次循环中处理一个循环项后并更新这个变量。
为了深入理解这个循环,我们把循环内部拆解为函数,找出相似之处
上面的代码两个循环非常相似,唯一不同的是初始值和循环中的调用的函数。两者都将数组最终降为单个值,所以我们自己写
reduce
但是 js 里面内置了
reduce
,我们直接拿来用就行了使用了原生的
reduce
分离了循环代码后,代码的复杂度降低了很多。Filter
map
可以很好的处理数组的每一项reduce
可以把一个数组缩减到一个单一值但是想处理一个数组中的多个项目呢?首先给
workers
数组加点数据两个问题:
用个普通的
for
循环:上面两块代码唯一不同的是
if
判断,所以把if
转换成函数我们在把这种只返回
true
false
的函数成为predicate
(谓词函数) 然后使用predicate
来决定数组中元素是否保留把
predicate
函数抽象到一个 filter 中同样 js 也内置了
filter
函数,直接使用只需要写一个功能单一而专注的过滤函数,然后调用
filter
即可。Find
如果需要找出 Sara ,
filter
是没问题的这样的代码并不高效,因为只有一个 Sara ,找到后就可以停止查找操作了。
所以写一个
find
函数, 返回第一个符合项find
函数实现非常简单,js 也内置了find
和
filter
一样,代码简洁且专注。最后
使用内置的这些迭代函数
map
reduce
filter
find
在上面每一个例子中,我们把问题分解,使用更小而纯粹的函数即可找到解决方案。
因为循环中总是在处理或者构建一个数组,或者两者同时在做,因此通过 js 内置的数组处理函数几乎可以消除代码中绝大多数循环,写出复杂性更低更利于维护的代码。