在 JavaScript 中,函数为一等公民(First Class),所谓的 “一等公民”,指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或作为其它函数的返回值。
- 函数赋值给变量 其实就是在《创建函数》一节中提到的函数表达式
函数作为参数
// js内置对象Array的实例方法map()
const array = [1, 2, 3, 4];
const map = array.map(x => x * 2); // [2, 4, 6, 8]
函数作为其他函数的返回值
高阶函数
在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数:
- 接受一个或多个函数作为输入;
- 输出一个函数。
接收一个或多个函数作为输入,即函数作为参数传递。这种应用场景,相信很多人都不会陌生。比如常用的javascript内置对象
Array
的两个实例方法Array.prototype.map()
和Array.prototype.filter()
都是高阶函数:
// Array.prototype.map 高阶函数
const array = [1, 2, 3, 4];
const map = array.map(x => x * 2); // [2, 4, 6, 8]
// Array.prototype.filter 高阶函数
const words = ['semlinker', 'kakuqo', 'lolo', 'abao'];
const result = words.filter(word => word.length > 5); // ["semlinker", "kakuqo"]
而输出一个函数,即调用高阶函数之后,会返回一个新的函数。我们日常工作中,常见的
debounce
和throttle
函数就满足这个条件,因此它们也可以被称为高阶函数。
函数组合
函数组合就是将两个或两个以上的函数组合生成一个新函数的过程:
const composeFn = function (f, g) {
return function (x) {
return f(g(x));
};
};
在以上代码中,f
和 g
都是函数,而 x
是组合生成新函数的参数。
函数组合的作用
在项目开发过程中,为了实现函数的复用,我们通常会尽量保证函数的职责单一,比如我们定义了以下功能函数:
在拥有以上功能函数的基础上,我们就可以自由地对函数进行组合,来实现特定的功能:
function lowerCase(input) {
return input && typeof input === "string" ? input.toLowerCase() : input;
}
function upperCase(input) {
return input && typeof input === "string" ? input.toUpperCase() : input;
}
function trim(input) {
return typeof input === "string" ? input.trim() : input;
}
function split(input, delimiter = ",") {
return typeof input === "string" ? input.split(delimiter) : input;
}
const trimLowerCaseAndSplit = compose(trim, lowerCase, split); // 参考下面compose的实现
trimLowerCaseAndSplit(" a,B,C "); // ["a", "b", "c"]
在以上的代码中,我们通过 compose
函数实现了一个 trimLowerCaseAndSplit
函数,该函数会对输入的字符串,先执行去空格处理,然后在把字符串中包含的字母统一转换为小写,最后在使用 ,
分号对字符串进行拆分。利用函数组合的技术,我们就可以很方便的实现一个 trimUpperCaseAndSplit
函数。
组合函数的实现
function compose(...funcs) {
return function (x) {
return funcs.reduce(function (arg, fn) {
return fn(arg);
}, x);
};
}
在以上的代码中,我们通过 Array.prototype.reduce 方法来实现组合函数的调度,对应的执行顺序是从左到右。这个执行顺序与 Linux 管道或过滤器的执行顺序是一致的。
不过如果你想从右往左开始执行的话,这时你就可以使用 Array.prototype.reduceRight 方法来实现。