转载
作者: 化辰
地址:http://taobaofed.org/blog/2017/03/16/javascript-functional-programing/
说明:原文章中第一个例子中的函数写法二有错误
JavaScript 函数式编程是一个存在了很久的话题,但似乎从 2016 年开始,它变得越来越火热。这可能是因为 ES6 语法对于函数式编程更为友好,也可能是因为诸如 RxJS (ReactiveX) 等函数式框架的流行。
看过许多关于函数式编程的讲解,但是其中大部分是停留在理论层面,还有一些是仅针对 Haskell 等纯函数式编程语言的。而本文旨在聊一聊我眼中的函数式编程在 JavaScript 中的具体实践,之所以是 “我眼中的” 即我所说的仅代表个人观点,可能和部分 严格概念 是有冲突的。
本文将略去一大堆形式化的概念介绍,重点展示在 JavaScript 中到底什么是函数式的代码、函数式代码与一般写法有什么区别、函数式的代码能给我们带来什么好处以及常见的一些函数式模型都有哪些。
基本的函数式编程
下面例子是一个具体的函数式体现
// 数组中每个单词,首字母大写
// 一般写法
const arr = ['apple', 'pen', 'apple-pen'];
for (const i in arr) {
// console.log(arr[i]);
const c = arr[i][0]; //获取首字母
// console.log(c);
arr[i] = c.toUpperCase() + arr[i].slice(1);
//那么 slice() 方法会选取从 start 到数组结尾的所有元素。
}
console.log(arr);
// 函数式写法一
function upperFirst(word) {
// console.log(word);
return word[0].toUpperCase() + word.slice(1);
}
function wordToUpperCase(arr) {
return arr.map(upperFirst);
}
console.log(wordToUpperCase(['apple', 'pen', 'apple-pen']));
//函数写法二
// console.log(arr.map(word => word));
console.log(arr.map(word => word[0].toUpperCase() + word.slice(1)));
函数式编程很好的解决了上述问题。首先参看 函数式写法一,它利用了函数封装性将功能做拆解(粒度不唯一),并封装为不同的函数,而再利用组合的调用达到目的。这样做使得表意清晰,易于维护、复用以及扩展。其次利用 高阶函数,Array.map 代替 for…of 做数组遍历,减少了中间变量和操作。
而 函数式写法一 和 函数式写法二 之间的主要差别在于,可以考虑函数是否后续有复用的可能,如果没有,则后者更优。
链式优化
从上面 函数式写法二 中我们可以看出,函数式代码在写的过程中,很容易造成 横向延展,即产生多层嵌套,下面我们举个比较极端点的例子。
// 计算数字之和
// 一般写法
console.log(1 + 2 + 3 - 4)
// 函数式写法
function sum(a, b) {
return a + b;
}
function sub(a, b) {
return a - b;
}
console.log(sub(sum(sum(1, 2), 3), 4));
本例仅为展示 横向延展 的比较极端的情况,随着函数的嵌套层数不断增多,导致代码的可读性大幅下降,还很容易产生错误。
在这种情况下,我们可以考虑多种优化方式,比如下面的 链式优化 。
// 优化写法 (嗯,你没看错,这就是 lodash 的链式写法)
const utils = {
chain(a) {
this._temp = a;
return this;
console.log(_temp);
},
sum(b) {
this._temp += b;
return this;
console.log(_temp);
},
sub(b) {
this._temp -= b;
return this;
console.log(_temp);
},
value() {
const _temp = this._temp;
this._temp = undefined;
return _temp;
console.log(_temp);
}
};
console.log(utils.chain(1).sum(2).sum(3).sub(4).value());