当我们想要使用一个函数时,通常情况下其实就是想要将一些功能、逻辑等封装起来以便使用。相信有一些编码基础的读者对于封装这个概念并不陌生。我们通常通过函数封装来达到我们想要做的事情。
例如,我想要计算任意三个数的和。我们就可以将这三个数作为参数封装一个简单的函数。
function add(a, b, c) {
return a + b + c;
}
当我们在未来再次想要计算三个数的和时,直接调用该方法即可。
add(1, 2, 3);
当然,当我们想要做的事情比较简单的时候,可能还看不出来封装成为函数之后带来的便利。如果我们想要做的事情稍微复杂一点呢。例如我想要计算一个数组中的所有子项目的和。
function mergeArr(arr) {
var result = 0;
for(var i = 0; i ++; i < arr.length) {
result += arr[i];
}
return result;
}
那么当我们不通过封装成为函数的方式,每次都用for循环去计算数组中所有子项的和时,代码量肯定就会偏多。封装之后,当我们想要再次做这件事情的时候,只需要一句话就行。
mergeArr([1, 2, 3, 4, 5]);
我想函数封装的意义大家肯定都应该非常明确。因此这里便不在多说。但是我们面临的问题是,当我们想要去封装一个函数时,如果做才是最好的呢?
如果你没有去认真想过这个问题,那么你封装的函数,可能会非常糟糕。也许使用起来并不是那么好用,甚至可能会导致你的程序里出现你无法预知的bug。因此实践中在做函数封装的时候,我们有必要学习一些优秀的封装习惯,来让自己的代码看上去更加的专业与可靠,而不是连自己都没那么有底气的封装。
需要提醒大家的是,我们需要忘记前面所有的demo中函数的编写风格。因为那仅仅只是为了方便大家理解闭包,this等只是列出来的简单例子。但是如果将这样的编写风格放在实践中的话,那么一定是一场灾难。
而我们这里将要学习的函数封装思维叫做函数式编程
而与函数式编程对应的一个风格叫做命令式编程。这也是我们在初学时,不由自主会使用的风格。
用一个简单的例子来区别这两种不同的思维。
我们在实践中常常需要处理各种不同的数据,假设这个时候有一个数组,我们需要找出该数组中所有类型为number的子项。
当我们使用命令式编程时,可能会这样想。
var array = [1, 3, 'h', 5, 'm', '4'];
var res = [];
for(var i = 0; i < array.length; i ++) {
if (typeof array[i] === 'number') {
res.push(array[i]);
}
}
在这种实现方式中,我们平铺直叙的达到了我们想要的目的。这样做的问题在于,当在另外一个时刻/场景,出现了同样的需求,或者我们还需要将另外一个数组中的number子项也找出来,那么如果用这种方式我们就不得不把实现逻辑再重写一遍。因此这个时候我们的代码就变得非常冗余且难以维护。
而函数式编程的思维则希望我们当遇到这种场景,需要将逻辑封装起来。
function getNumbers(array) {
var res = [];
array.forEach(function(item) {
if (typeof item === 'number') {
res.push(item);
}
})
return res;
}
// 以上是我们的封装,以下是功能实现
var array = [1, 3, 'h', 5, 'm', '4'];
var res = getNumbers(array);
在函数式编程的实践中,我们封装了一个工具方法,专门用来找出一个数组中的所有number。而我们真正需要维护的代码则仅仅只有两行。我们只需要知道getNumbers
这个工具方法能干什么,而不关心它的内部如何实现。这样代码会简洁很多,维护起来也非常简单。
在现实生活中这种思维的场景也非常多见。例如我们点外卖。我们只需要在app里下单,然后等待外卖小哥把餐送到我们手里就行了。我们不用关注我们点的餐是如何做出来的,也不用关注外卖小哥是如何送过来的。这种更加关心结果的思维方式,正是函数式编程。
OK,通过一些简单的例子了解了函数式编程之后,下一章我们将会更加详细的学习函数式编程的思维。