今天看的是函数式编程的相关文章,之前虽然没接触过函数式编程,但是这并不能阻止我们读文章啊。
文章是这篇 Pointfree Javascript
首先肯定先来理解一下标题的意思,然后在阮一峰的博客中找到了这个
Pointfree:不使用所要处理的值,只合成运算过程。中文可以译作"无值"风格。
文章在一开始的时候先抛出了两段代码,代码如下:
// imperative style
var getAdminEmails = function(users) {
var emails = [];
for (var i = 0; i < users.length; i++) {
if (users[i].role === 'admin') {
emails.push(users[i].email);
}
}
return emails;
}
var getAdminEmails = users =>
users
.filter(u => u.role === 'admin')
.map(u => u.email);
大家可以思考一下这两段代码的区别。
第一段代码的逻辑是我们大多数人在写代码的时候会使用的,先声明一个空的数组,然后再去逐个比对,是不是admin的身份。如果是,就讲这个邮箱,放入数组中。最后返回数组。
而第二段代码的逻辑是根据正常的思维逻辑来写的。它先拿到所有的user,然后使用js原生的filter来取出所有的role是admin的user,最后返回这些user的email。
第二段代码的风格的一个优点是函数的代码更接近函数的描述。这使得它更容易理解和理解它。当其他人来研究你写的新的代码的时候,能够快速了解写了什么、运行程序的时候发生了什么。
但是这第二段代码不是Pointfree的最终想要得到的代码,于是下面又附上了作者想要得出最终想要的代码的思考过程。
作者想了一下,最好我只要声明一下这个函数就能得到结果。
var getAdminEmails = compose(getTheEmailsOf, onlyTheAdminRoleUsers);
于是作者开始实现这两个函数,最终的结果是这样。
作者先大致的介绍了一下需要使用什么基本方法。(其中compose和map是有原生方法的,但作者应该只是想大致介绍一下)
var compose = (f, g) => x => f(g(x));
var prop = p => x => x[p];
var map = f => list => list.map(f);
// which lead to:
var getTheEmailsOf = map(prop('email'));
然后是进一步的封装。
var propEq = v => p => obj =>
prop(p)(obj) === v;
var filter = f => list =>
list.filter(f);
var onlyTheAdminRoleUsers =
filter(propEq('admin')('role'));
最后是需要的函数的声明。
var getAdminEmails = compose(
map(
prop('email')),
filter(
propEq('admin')('role')));
这就是所谓的Pointfree编程或tacit编程。这个版本完全是由其他的较小、可复用的功能组成的。这些功能不仅可以帮助我们快速完成下一个功能,而且可以帮助我们更快地理解所有的功能。当你知道什么是prop函数后,会觉得prop('something')比obj => obj.something好理解多了。
总结
Pointfree编程就是通过组合使用较小、通用、定义良好且经过良好测试的功能来构建所需的功能进行编程的方法。
也不必担心临时变量,因为他会使得代码更容易理解,更难以引入错误。此外,由于代码的段落较小,使得它更容易理解、更方便测试,这使它更可靠。
当然这不是一个万金油,因为很多时候,会存在一个完全无关的功能。我发现它的主要弱点在于函数具有多个参数的时候。很多时候它会导致有一大部分non Pointfree的代码。但是总体来说,利大于弊。