前面的话
什么是高阶函数?这篇文章从定义开始介绍什么是高阶函数。
高阶函数
高阶函数(Higher-order function)一般指操作函数的函数,至少满足下列条件之一:
- 接受一个或多个函数作为输入
- 输出一个函数
高阶函数是对其他函数进行操作的函数,可以将它们作为参数传入,或者返回。即高阶函数是一个接受函数作为参数传递或者将函数作为返回值输出的函数
参数传递
把函数作为参数传递,代表可以抽离出一部分容易变化的业务逻辑。我们常见的场景就是回调函数,在ajax异步请求的应用中,回调函数的使用非常频繁。
[回调函数]
var getUserInfo = function (userId, callback) {
$.ajax('http://xx.com/getUserInfo?'+userId, function (data) {
if(typeof callback === 'function') {
callback(data);
}
})
}
getUserInfo(123, function(data) {
alert(data.userName)
})
[数组排序]
函数作为参数传递的另外一个场景数组排序函数sort(),Array.prototype.sort接受一个函数作为参数, 这个函数里实现的是数组元素的排序方法。目的是对数组进行排序,但使用什么规则去排序,那就要看这个函数参数是怎样封装的。
// 从小到大
var values = [1,4,7,9];
values.sort(function(a, b) {
return a - b;
});
console.log(values);
// 从大到小
values.sort(function(a, b) {
return b - a ;
})
console.log(values);
js为数组定义了5个迭代方法,它们都接受两个参数:在每一项上运行的函数和运行该函数的作用域对象(可选)例如, Array.prototype.map,Array.prototype.filter。 js还有两个归并数组的方法reduce与reduceRight,它们接受两个参数:一个在每一项上调用的函数和(可选的)作为归并基础的初始值。可知这些都属于高阶函数。
下面对比一下这些函数在不使用高阶函数与使用高阶函数的区别:
[Array.prototype.map()]
map()方法返回一个新数组,而这个数组的每一项都是在原数组中对应项上运行传入的函数后返回的结果,原数组不变。
传递给map的函数接受三个参数:数组选项的值、(可选)该项在数组中的位置、(可选)数组对象本身。
例如:给数组的每一项乘以2,然后返回新的数组。使用与不使用高阶函数区别:
// 使用高阶函数:
{
const numbers = [1, 2, 3, 4, 5];
const mapResult = numbers.map( (item) => item * 2);
console.log(mapResult);
// 不使用高阶函数:
const arr1 = [1, 2, 4, 5, 6];
const arr2 = [];
for(let i = 0;i < arr1.length; i++){
arr2.push(arr1[i] * 2);
}
console.log(arr2);
}
[Array.prototype.filter()]
filter(过滤器)方法返回一个新数组,它利用传入的函数确定是否在返回的新数组中包含某一项(原数组中通过测试的 元素),原数组不变。传递给filter()的参数函数同样也接受三个参数。
例如:有一个数组[1, 2, 3, 3, 3, 2 ,4 ,1, 5 ,4],想得到一个新数组,要求这个数组在原数组基础上去重。
// 使用高阶函数
{
const arr1 = [1, 2, 3, 3, 3, 2 ,4 ,1, 5 ,4];
const filterResult = arr1.filter((item, index, array ) => {
// indexOf只返回查找的项所在的第一个位置
return array.indexOf(item) === index;
} )
console.log(filterResult);
//不使用高阶函数
const arr2 = [1, 2, 3, 3, 3, 2 ,4 ,1, 5 ,4];
const arr3 = [];
for(let i = 0; i< arr2.length; i++) {
if(arr2.indexOf(arr2[i]) === i){
arr3.push(arr2[i]);
}
}
console.log(arr3);
}
[Array.prototype.redece()]
reduce()方法的两个参数:一个在每一项上调用的函数和(可选的)作为归并基础的初始值。reduce()方法最终将返回一个单一值。
对数组中的每一个元素执行一个传递的回调函数,这个回调函数接受4个参数:前一个值(prev),当前值(cur), index,array。除了回调函数,还可以接受一个初始值initialValue值。
- 如果没有技工initialValue值,那么第一次调用callback函数时,prev使用原数组的第一个元素,cur使用数组的第二个元素。不能再没有初始值的空数组上调用reduce()
- 如果提供了initialValue,那么将作为第一次调用callback函数时的prev值,cur值为数组的第一个元素。
例如,利用reduce()方法计算数组中所有值之和:
{
// 使用高阶函数(无initialValue值)
const arr1 = [1, 2, 3, 4, 5];
const redeceResult = arr1.reduce((prev, cur, index, array) => {
return prev + cur;
})
console.log(redeceResult);
// 使用高阶函数(无initialValue值)
const arr2 = [1, 2, 3, 4, 5];
const redeceResult1 = arr1.reduce((prev, cur, index, array) => {
return prev + cur;
}, 10)
console.log(redeceResult1);
// 不使用高阶函数
const arr3 = [1, 2, 3, 4, 5];
var result = 0;
for(let i = 0; i < arr2.length; i++){
result += arr2[i];
}
console.log(result);
}
返回值输出
相比把函数当作参数传递,函数作为返回值输出的场景也有很多。让函数继续返回一个可执行的函数,意味着运算过程是可延续的。
[isType函数]
使用Object.prototype.toString.call()方法来判断数据类型的一系列的isType函数
{
var isString = function(obj) {
return Object.prototype.toString.call(obj) === '[object String]';
}
var isArray = function(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
}
var isNumber = function (obj) {
return Object.prototype.toString.call(obj) === '[object Number]';
}
console.log(isString('123'));
console.log(isArray([1,2,3]));
console.log(isNumber(123));
}
实际上,上面的代码有重复的代码,不同的是Object.prototype.toString.call(obj)返回的字符串。
可以把这些字符串作为参数提前传入isType函数。
var isType = function (type) {
return function (obj) {
return Object.prototype.toString.call(obj) === '[object ' + type +']';
}
}
var isString = isType('String');
var isArray = isType ('Array');
var isNumber = isType('Number');
console.log(isString('123'));
这就是高阶函数,因为这里isType函数返回的结果是一个函数obj => {…}
[not 函数]
当数组的每个元素都是奇数时,返回true
function not(f) {
return function () {
return !(f.apply(this, arguments));
};
}
//偶数时,返回true;奇数时,返回false
var even = function (x) {
return x % 2 === 0;
}
//偶数时,返回false;奇数时,返回true
var odd = not(even);
console.log( [1, 1, 3, 5, 5].every(odd)); // true
[mapper函数]
每个数组元素执行函数f(),并返回所有计算结果组成的数组
function mapper(f){
return function(){
return f.apply(this,arguments);
}
}
var increment = function(x){
return x + 1;
}
var incrementer = mapper(increment);
console.log([1, 2 ,3 ].map(incrementer));