★本文引自:廖雪峰的官方网站
- 高阶函数:即一个函数可以接收另一个函数作为参数,这种函数就称之为高阶函数。
function add(x,y,fn) {
return fn(x) + fn(y);
}
console.info(add(-5,6,Math.abs));
当我们调用add(-5,6,Math.abs)时,结果会输出11
;
编写高阶函数,就是让函数的参数能够接收别的函数。
案例一:高阶函数 - 数组Map遍历
比如我们有一个f(x) = x * x;要把这个函数作用在一个数组[1,2,3,4,5,6,7,8,9]
上,就可以用map
实现如下:
function pow(x) {
return x * x;
}
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
console.info(arr.map(pow)); //输出 -> [1, 4, 9, 16, 25, 36, 49, 64, 81]
map()
传入的参数是pow
,即函数对象本身。
你可能会想,不需要map()
,写一个循环,也可以计算结果:
var fn = function(x) {
return x * x;
}
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var result = [];
for(var i=0;i < arr.length; i++) {
result.push(fn(arr[i]));
}
console.info(result); //[1, 4, 9, 16, 25, 36, 49, 64, 81]
的确可以,但是,从上面的循环代码,我们无法一眼看明白把f(x)作用在Array的每个元素并把结果生成一个新的Array
。
所以,map()
作为高阶函数,事实上它把运算规则抽象了,因此,我们不但可以计算简单的f(x)=x * x,还可以计算任意复杂的函数,比如把Array
的所有数字转为字符串:
//只需要一行代码
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
console.info(arr.map(String));
案例二:高阶函数 - 数组reduce
再看reduce的用法。Array的reduce()
把一个函数作用在这个Array
的[x1, x2, x3...]
上,这个函数必须接受两个参数,reduce()
把结果继续和序列的下一个元素做累积计算,其效果就是:
[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)
比方说对一个Array
求和,就可以用reduce
实现:
var arr = [1, 3, 5, 7, 9];
var sum = arr.reduce(function(x, y){
return x + y;
});
console.info(sum);
案例三:高阶函数 — 数组filter
filter也是一个常用的操作,它用于把Array
的某些元素过滤掉,然后返回剩下的元素。
和map()
类似,Array
的filter()
也接收一个函数。和map()
不同的是,filter()把传入的函数依次作用每个元素,然后根据返回值是true
还是false
决定保留还是丢弃该元素。
例如,在一个Array
中,删除掉偶数,只是留奇数,可以这么写:
var arr = [1, 2, 4, 6, 9, 10, 15];
var r = arr.filter(function(x){
return x % 2 !==0;
});
console.info(r); //输出 -> [1, 9, 15]
- 把一个
Array
中的空字符串删除掉,可以这么写:
var arr = ['A','','B',null,undefined,'C',' '];
var r = arr.filter(function(s){
return s && s.trim();
});
console.info(r); //输出 -> ["A", "B", "C"]
- 我们利用
filter
,可以巧妙地去除Array
的重复元素,代码实现如下:
var r,arr = ['apple','strawberry','banana','pear','apple','orange','orange','strawberry'];
console.info(arr.indexOf('orange')); //输出 -> 5,从左往右数第一个元素下标
var i = 0;
r = arr.filter(function(element, index, self){
console.info("第" + i +"个元素:" + (self.indexOf(element) === index));
//依次输出:
//第0个元素:true
//第1个元素:true
//第2个元素:true
//第3个元素:true
//第4个元素:false
//第5个元素:true
//第6个元素:false
//第7个元素:false
i++;
return self.indexOf(element) === index;
});
console.info(r); //输出 -> ["apple", "strawberry", "banana", "pear", "orange"]
上面代码实现去除元素依靠的是indexOf
总是返回第一个元素的位置,后续的重复元素位置与indexOf
返回的位置不相等,因此被filter
滤掉。
高阶函数 — sort排序算法
排序也是在程序中经常用到的算法。无论使冒泡排序还是快速排序,排序的核心是比较两个元素的大小。如果是数字,我们可以直接比较,但如果是字符串或者两个对象呢?直接比较数学上的大小是没意义的,因此,比较过程必须通过函数抽象出来。通常规定,对于两个元素x
和y
,如果认为x<y
,返回-1
,如果认为x==y
,返回0
,如果x>y
,则返回1
,这样,排序算法就不用关心具体的比较过程,而是根据比较结果直接排序。
JavaScript的Array
和sort()
方法就是用于排序的,但是排序结果可能大吃一惊:
/*看上去正常的结果: */
console.info(['Google', 'Apple', 'Microsoft'].sort()); //["Apple", "Google", "Microsoft"]
/*apple排在了最后:*/
console.info(['Google', 'apple', 'Microsoft'].sort()); //["Google", "Microsoft", "apple"]
/*无法理解的结果:*/
console.info([10, 20, 1, 2].sort()); //[1, 10, 2, 20]
第二个排序把apple
排在了最后,是因为字符串根据ACSII码进行排序,而小写字母a
的ASCII码在大写字母之后。
第三个排序结果是什么鬼?简单的数字排序都能错?
这是因为Array
的sort()
方法默认把所有元素先转换为String再排序,结果10
排在了2
的前面,因为字符1
比字符2
的ASCII小。
如果不知道sort()
方法的默认排序规则,直接对数字排序,绝对栽进坑里!
幸运的是,sort()
方法也是一个高阶函数,它还可以接收一个比较函数来实现自定义的排序。
要按数字大小排序,我们可以这么写:
var arr = [10, 20, 1, 2];
var result = arr.sort(function(x ,y){
console.info(x ,y, arr); //重做往右比较,然后
if(x < y) {
return -1; //排序是两个比较之后的值-1,1,0来决定比较元素的位置是向左、向右或者保持不变
}
if(x > y) {
return 1;
}
return 0;
});
console.info(arr); //[1, 2, 10, 20]
console.info(arr === result); //返回为true,表明arr和result是同一对象,上面排序对原`Array`进行了修改