源起
函数式编程近几年异常盛行,常常能够在网上看到他人议论相干话题. 我机缘巧合之下在github上看到有人提到一个讲js函数式编程的视频,看过以后倏忽恍然大悟,霎时把之前细碎的关于函数式编程的学问一会儿都联系了起来, 因而本身愿望趁有空把这些学问总结一下, 如许既能够回忆下学问耶没准能帮到一些对函数式编程感兴趣的朋友们.
为何须要函数式编程
实在千万别被这看似深邃的名字吓怕了, 实在我们码农用这类编程头脑不过是为了能更爽的写好代码. 总结一下函数式编程的长处有以下两个长处:
Less bug: 可读性高, 逻辑上更轻易明白.
less Time: 更高的笼统层级, 代码高度可复用.
高阶函数(Higher-order functions)
这里又一个炫酷屌的名字: “高阶函数”. 别怕, 本文并不触及高等数学的学问就能够让你相识高阶函数的观点. 然则等等! 高阶函数跟函数式编程又有毛关联? 实在简朴的说就是高阶函数的特征让我们能够完成函数式编程.
空话不多说, 下面就简朴说下什么是高阶函数:
Functions that operate on other functions, either by taking them as arguments or by returning them, are called higher-order functions.
从定义能够晓得高阶函数就是能够把函数作为变量传入别的函数, 或许一个函数的返回值是一个函数的函数. 假如上面的话把你绕晕了那末我来简朴的归纳综合下就是函数能够被看成变量在顺序中传来传去. 下面举几个例子:
//把函数当做变量
var foo = (x) => console.log(x)
//把函数看成参数传入另一个函数
var bar = (y, fn) => fn(y)
foo('FP is good') // FP is good
bar('FP is great', foo) //FP is great
下面这个例子更能表现高阶函数的便利性, 假如我们要遍历数组而且打印数组元素的信息, 你会怎么做?
var arr = [1,1,2,3,5,8]
function traverseArray(arr) {
for (let i = 0; i < arr.length; i++) {
console.log(`index: ${i}, value: ${arr[i]}`)
}
}
traverseArray(arr)
Easy enough! 假如用函数式编程的头脑重写上面的代码应该是这个模样的:
function forEach(arr, fn) {
for (let i = 0; i < arr.length; i++) {
fn(i, arr[i], arr)
}
}
forEach(arr, (index, value) => console.log(`index: ${index}, value: ${value}`))
WTF!? 说好的函数式编程更简约代码量更少呢! 显著第二种写法写的代码更多啊. 别急, 实在es5的时刻JS已把一系列高阶函数设置好了,比方forRach, map, reduce, filter, every..
让我们用js的array.forEach
重写上面的代码:
arr.forEach((v, k) => console.log(`index: ${k}, value: ${v}`))
怎样, 一样代码处置惩罚战役。 或许看到这里你照样以为没什么意义,毕竟虽然代码量少了些,但也没觉得有什么实质性的变化. 下面我会举几个比较罕见的例子,经由过程例子让我们一同体味这类风趣的编程头脑,或许看过以后你的主意会有所转变.
经常使用要领举例
先引见下几个经常使用的函数, filter, map, reduce:
var animals = [
{name: 'a' , species: 'dog', weight: 11},
{name: 'b', species: 'cat', weight: 10},
{name: 'c', species: 'fish', weight: 1},
{name: 'd', species: 'cat', weight: 8},
{name: 'e', species: 'rabbit', weight: 3}
]
// 找到一切品种为猫的动物
animals.filter((animal) => animal.species === 'cat')
// [ { name: 'b', species: 'cat' }, { name: 'd', species: 'cat' } ]
// 返回一切动物的名字
animals.map((animal) => animal.name)
// [ 'a', 'b', 'c', 'd', 'e' ]
// 动物总重
animals.reduce((pre, animal) => pre + animal.weight, 0)
//33
怎样? 假如用轮回来写一定不可能一行代码就搞定吧. 让我们看一些更深切更有意义的例子吧.
比方这道比较典范的算法题: 给定一个字符串”abcdaabc”统计每一个字符的涌现次数.
// 平常做法是如许的
var str="abcdaabc"
var count = {};
var i;
for(i=0;i<str.length;i++){
var chr = str.charAt(i);
if( typeof count[chr] === "undefined"){
count[chr] = 1;
}else{
count[chr]++;
}
}
// 运用函数式编程头脑的要领是如许的
var res = str.split('')
.reduce((pre, cur) => (pre[cur]++ || (pre[cur] = 1), pre), {})
由上面的例子可见, 高阶函数的特征让我们在处置惩罚这类题目时比一般的要领要轻易的多. 在更现实的运用场景中函数式编程的便利性更能充足的表现, 个中一个典范的例子是统计文本中涌现频次最高的十个单词:
var fs = require('fs');
var content = fs.readFileSync('words.txt').toString();
var words = content.split(/[\s.,\/:\n]+/);
// 把单词悉数变成小写并运用上一个例子的要领统计单词涌现的次数
var tally = words.map((word) => word.toLowerCase())
.reduce((pre, cur) => (pre[cur]++ || (pre[cur]=1), pre), {})
//把object的key变成数组并举行排序
var top10 = Object.keys(tally)
.map((key) => {
return {word: key, count: tally[key]}
})
.sort((a, b) => b.count - a.count)
.slice(0, 10)
console.log(top10)
上面这个例子充足表现了函数式编程头脑的魅力,代码精简而且可读性高! 想想假如你要用轮回来写有多若干行代码!
好了, 总结终了, 末了附上参考资料.
参考资料
Functional programming in JavaScript
Functional Programming By Example
Eloquent javascript – chapter6