function add(x,y,f){
return f(x) + f(y);
}
//将Math.abs作为f传入add函数
var x = add(-5,6,Math.abs);
console.log('add:'+x);
如果我们想把一个函数f(x)=X2作用在一个数组[1,2,3,4,5,6]中,那么要怎么实现呢?
首先先用传统的方法来实现
//定义一个函数
var f = function(x){
return x * x;
}
var arr = [1,2,3,4,5,6];
var result = [];
for (var i = 0; i < arr.length; i++) {
//将调用函数的结果push到新的数组中
result.push(f(i));
}
//遍历结果数组
console.log('传统方式:'+result);
由于map()方法定义在JavaScript的Array中,我们调用Array的map()方法,传入到我们自己的数组中,
//定义一个函数
function pow(x){
return x * x;
}
var arr = [1,2,3,4,5,6];
var result = arr.map(pow);
console.log('map方式:'+result);
我们这样做一行就搞定了,是不是特别的方便!当然我们也可以有其他的功能,比如:
将Array中的所有数字转为字符串:
var arr = [1,2,3,4,5,6];
console.log(arr.map(String));
var arr=['1','2','3'];
var r;
r = arr.map(Number);
再看reduce的用法,Array的reduce()把一个函数作用在这个Array的【x1,x2,x3…】上,这个函数必须接收两个参数.
比如对一个Array求和,就可以用reduce实现:
var arr = [1,2,3,4,5,6];
console.log(arr.reduce(function(x,y){
return x + y;
}));
要把[1,2,3,4,5]变换成整数12345,reduce()也能派上用场:
filter是一个常用的操作,它用于把Array的某些元素过滤掉,然后返回剩下的元素
和map()类似,Array的filter()也接收一个函数。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是true还是false决定保留还是丢弃该元素。
例如:在一个Array中,删掉偶数,只保留奇数,可以写:
var arr = [1, 2, 4, 5, 6, 9, 10, 15];
var r = arr.filter(function(x){
return x % 2 !== 0;
});
console.log(r);
var arr = ['A', '', 'B', null, undefined, 'C', ' '];
var r = arr.filter(function(s){
return s && s.trim();
});
console.log(r);
由此可以看出filter()这个高阶函数,关键在于正确实现一个“筛选”函数
filter()接收的回调函数,其实可以有多个参数 ,通常我们仅使用 第一个参数 表示元素的位置和数组本身:
var arr =['a','b','c'];
/**
* element 数组中的目标元素
* index 数组中的元素的索引
* index 整个数组
*/
var r = arr.filter(function(element,index,self){
console.log(element);
console.log(index);
console.log(self);
return true;
});
var
r,arr = ['apply','strawberry','banana','pear','apply','orange','orange','strawberry'];
r = arr.filter(function(element,index,self){
//数组元素对应的目标元素和数组中索引相同时就返回,这样就达到了去掉重复项
return self.indexOf(element) == index;
});
console.log(r.toString());
排序算法
排序也是程序中经常用到的算法,无论使用冒泡排序还是快速,排序的核心是比较两个元素的大小。如果是数字,我们可以直接比较,但如果字符串或者两个对象呢?直接 比较数学上的大小是没有意义的,因此,比较的过程必须通过函数抽象出来。通常规定,对于两个元素x和y,如果认为x
JavaScript的Array的sort()方法就是使用排序的,但是排序结果可能让你大吃一惊:
var arr = [1,34,6,0];
console.log(arr.sort());
var arr = [1,34,6,0];
// console.log(arr.sort());
arr.sort(function(x,y){
if(x < y){
return -1;
}
if(x > y){
return 1;
}
return 0;
});
console.log(arr);
var arr = [1,34,6,0];
// console.log(arr.sort());
arr.sort(function(x,y){
if(x > y){
return -1;
}
if(x < y){
return 1;
}
return 0;
});
console.log(arr);
默认情况下,对字符串排序,是按照ASCII的大小比较的,现在,我们提出排序应该忽略大小写,按照字母排序,要实现这个算法,不必对现在代码在加改动,只要我们能定义出忽略大小写的比较算法就可以实现排序了:
var arr = ['Google', 'apple', 'Microsoft'];
arr.sort(function(s1,s2){
//将字母转换为大写
x1 = s1.toUpperCase();
x2 = s2.toUpperCase();
if(x1 < x2){
return -1;
}
if(x1 > x2){
return 1;
}
return 0;
});
console.log(arr);
对于数组,除了map()、reduce、filter()、sort()这些方法可以传入一个函数外,Array对象还提供了很多非常实用的高阶函数。
every()方法可以判断数组的所有元素是否满足测试条件
例如:
判断当前数组中的元素长度是否大于0
var arr = ['Apple','pear','orange'];
console.log(arr.every(function (s){
return s.length > 0;
}));//true,每个元素都满足s.length>0
console.log(arr.every(function (s){
return s.toLowerCase() === s;
}));//false,因为不是每个元素都全部是小写
find()方法用于查找符合条件的第一个元素,如果找到 了,返回这个元素,否则,返回undefined;
var arr = ['Apple','pear','orange','HKK'];
console.log(arr.find(function (s){
return s.toLowerCase() === s;
}));//'pear',因为pear全部是小写
console.log(arr.find(function (s){
return s.toUpperCase() === s;
}));//'HKK',因为HKK全部是大写的
var arr = ['Apple','pear','orange','HKK'];
console.log(arr.findIndex(function (s){
return s.toLowerCase() === s;
}));//1.因为'pear'的索引是1
console.log(arr.findIndex(function (s) {
return s.toUpperCase() === s;
}));//3,因为'HKK'的索引是3
forEach()和map()类似,它也把每个元素依次作用于传入的函数,但不会返回新的数组。forEach()常用于遍历数组,因此,传入的函数不需要返回值:
var arr = ['Apple','pear','orange','HKK'];
arr.forEach(console.log);//依次打印 每个元素
//我们不是立刻得到进行求和,而是在后面的代码中使用,就先保存求和的函数!
function lazy_sum(arr) {
//定义一个函数用于求和
var sum = function(){
return arr.reduce(function (x,y) {
return x + y;
});
}
return sum;
}
//调用lazy_sum时其实是[Function: sum]函数
var f = lazy_sum([1,2,3,4,5,6,7,8,9,10]);
//这里才是真正的调用
console.log(f());
总结:在上面这个例子中,我们在函数 lazy_sum中又定义了一个函数sum,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时 ,相关参数和变量都保存在返回的函数 中,这种称为"闭包(Closure)"的程序结构拥有极大的威力。
这两次的调用每次都会返回一个新的函数,虽然传入的是相同 的参数 ,但是调用的结果不互相影响!
注意到返回的函数在其定义内部引用了局部变量arr,所以,当现代战争函数 返回一个函数后,其内部的局部变量还被新函数引用,所以,闭包用起来简单,实现起来可没有那么容易。
另一个需要注意的问题是,返回的函数 并没有立即执行,而是直到调用了f()才执行,我们来看一个例子:
function count() {
var arr = [];
for(var i = 1;i<= 3;i++){
arr.push(function () {
return i * i;
});
}
return arr;
}
var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];
console.log(f1());
console.log(f2());
console.log(f3());
你可能认为调用的结果为1,4,9,只是实际的结果是:
原因在于,返回的函数引用了变量i,但它并非立刻执行。而是3个函数 都返回时,它们所引用的变量i已经变成 了4,所以最终的 结果为16.
返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:
function count() {
var arr = [];
for(var i = 1;i<= 3;i++){
arr.push((function (n) {
return function () {
return n * n;
}
})(i));
}
return arr;
}
var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];
console.log(f1());
console.log(f2());
console.log(f3());
(function (x) {
return x * x;
})(3);
闭包可以返回一个函数 延迟执行,但是他还有更强大 的功能:
在面向对象的程序设计语言里,比如Java和C++,要在对象内部封装一个私有变量,可以用private 修饰一个成员变量。
在没有class机制,只有函数 的语言里,借助闭包,同样可以封装一个私有变量。我们用js创建一个计数器:
function create_counter(initial) {
var x = initial || 0;
return{
inc:function () {
x += 1;
return x;
}
}
}
var c1 = create_counter();
console.log(c1.inc());
console.log(c1.inc());
console.log(c1.inc());
在返回的对象中,实现了一个闭包,该闭包携带了局部变量x,并且,从外部代码根本无法访问到变量x。换句话说,闭包就是携带状态的函数,并且它的状态可以完全对外隐藏起来。
例如,要计算xy可以用Math.pow(x, y)函数,不过考虑到经常计算x2或x3,我们可以利用闭包创建新的函数pow2和pow3:
//定义一个函数,内部含有一个函数
function make_pow(n){
return function (x) {
return Math.pow(x,n);
}
}
var pow2 = make_pow(2);
var pow3 = make_pow(3);
console.log(pow2(5));
console.log(pow3(7));