深入理解javascript函数进阶系列第一篇——高阶函数
前面的话
前面的函数系列中介绍了函数的基础用法。从本文开始,将介绍javascript函数进阶系列,本文将详细介绍高阶函数
定义
高阶函数(higher-order function)指操作函数的函数,一般地,有以下两种情况
1、函数可以作为参数被传递
2、函数可以作为返回值输出
javascript中的函数显然满足高阶函数的条件,在实际开发中,无论是将函数当作参数传递,还是让函数的执行结果返回另外一个函数,这两种情形都有很多应用场景。下面将对这两种情况进行详细介绍
参数传递
把函数当作参数传递,代表可以抽离出一部分容易变化的业务逻辑,把这部分业务逻辑放在函数参数中,这样一来可以分离业务代码中变化与不变的部分。其中一个常见的应用场景就是回调函数
【回调函数】
在ajax异步请求的应用中,回调函数的使用非常频繁。想在ajax请求返回之后做一些事情,但又并不知道请求返回的确切时间时,最常见的方案就是把callback函数当作参数传入发起ajax请求的方法中,待请求完成之后执行callback函数
var getUserInfo = function( userId, callback ){
$.ajax( 'http://xx.com/getUserInfo?' + userId, function( data ){
if ( typeof callback === 'function' ){
callback( data );
}
});
}
getUserInfo( , function( data ){
alert ( data.userName );
});
回调函数的应用不仅只在异步请求中,当一个函数不适合执行一些请求时,也可以把这些请求封装成一个函数,并把它作为参数传递给另外一个函数,“委托”给另外一个函数来执行
比如,想在页面中创建100个div节点,然后把这些div节点都设置为隐藏。下面是一种编写代码的方式:
var appendDiv = function(){
for ( var i = ; i < ; i++ ){
var div = document.createElement( 'div' );
div.innerHTML = i;
document.body.appendChild( div );
div.style.display = 'none';
}
};
appendDiv();
把div.style.display = 'none'的逻辑硬编码在appendDiv里显然是不合理的,appendDiv未免有点个性化,成为了一个难以复用的函数,并不是每个人创建了节点之后就希望它们立刻被隐藏
于是把div.style.display = 'none'这行代码抽出来,用回调函数的形式传入appendDiv方法
var appendDiv = function( callback ){
for ( var i = ; i < ; i++ ){
var div = document.createElement( 'div' );
div.innerHTML = i;
document.body.appendChild( div );
if ( typeof callback === 'function' ){
callback( div );
}
}
};
appendDiv(function( node ){
node.style.display = 'none';
});
可以看到,隐藏节点的请求实际上是由客户发起的,但是客户并不知道节点什么时候会创建好,于是把隐藏节点的逻辑放在回调函数中,“委托”给appendDiv方法。appendDiv方法当然知道节点什么时候创建好,所以在节点创建好的时候,appendDiv会执行之前客户传入的回调函数
【数组排序】
函数作为参数传递的另一个常见场景是数组排序函数sort()。Array.prototype.sort接受一个函数当作参数,这个函数里面封装了数组元素的排序方法。目的是对数组进行排序,这是不变的部分;而使用什么规则去排序,则是可变的部分。把可变的部分封装在函数参数里,动态传入Array.prototype.sort,使Array.prototype.sort方法成为了一个非常灵活的方法
// 从小到大排列,输出: [ 1, 3, 4 ]
[ , , ].sort( function( a, b ){
return a - b;
}); // 从大到小排列,输出: [ 4, 3, 1 ]
[ , , ].sort( function( a, b ){
return b - a;
});
返回值输出
相比把函数当作参数传递,函数当作返回值输出的应用场景也有很多。让函数继续返回一个可执行的函数,意味着运算过程是可延续的
下面是使用Object,prototype.toString方法判断数据类型的一系列的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]';
};
实际上,这些函数的大部分实现都是相同的,不同的只是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( isArray( [ , , ] ) ); // 输出:true
当然,还可以用循环语句,来批量注册这些 isType 函数:
var Type = {};
for ( var i = , type; type = [ 'String', 'Array', 'Number' ][ i++ ]; ){
(function( type ){
Type[ 'is' + type ] = function( obj ){
return Object.prototype.toString.call( obj ) === '[object '+ type +']';
}
})( type )
};
Type.isArray( [] ); // 输出:true
Type.isString( "str" ); // 输出:true
AOP
AOP(面向切面编程)的主要作用是把一些跟核心业务逻辑模块无关的功能抽离出来,这些跟业务逻辑无关的功能通常包括日志统计、安全控制、异常处理等。把这些功能抽离出来之后,再通过“动态织入”的方式掺入业务逻辑模块中。这样做的好处首先是可以保持业务逻辑模块的纯净和高内聚性,其次是可以很方便地复用日志统计等功能模块
通常,在javascript中实现AOP,都是指把一个函数“动态织入”到另外一个函数之中。下面通过扩展Function.prototype来实现
Function.prototype.before = function (beforefn) {
var _this = this; // 保存原函数的引用
return function () { // 返回包含了原函数和新函数的"代理"函数
beforefn.apply(this, arguments); // 先执行新函数,修正this
return _this.apply(this, arguments); // 再执行原函数
}
};
Function.prototype.after = function (afterfn) {
var _this = this;
return function () {
var ret = _this.apply(this, arguments); //先执行原函数
afterfn.apply(this, arguments); //再执行新函数
return ret;
}
};
var func = function () {
console.log();
};
func = func.before(function () {
console.log();
}).after(function () {
console.log();
});
func();
把负责打印数字1和打印数字3的两个函数通过AOP的方式动态植入func函数。通过执行上面的代码,控制台顺利地返回了执行结果1、2、3
//1
//2
//
其他应用
【not】
下面的not函数用于返回参数的返回值的逻辑非
function not(f) {
return function () {
return !(f.apply(this, arguments));
};
}
//偶数时,返回true;奇数时,返回false
var even = function (x) {
return x % === ;
}
//偶数时,返回false;奇数时,返回true
var odd = not(even);
[, , , , ].every(odd);//true
【mapper】
下面的mapper()函数,返回的新函数将一个数组映射到另一个使用这个函数的数组上
//所返回的函数的参数应当是一个实参数组,并对每个数组元素执行函数f(),并返回所有计算结果组成的数组
function mapper(f){
return function(a){
return Array.prototype.map.call(a,f);
}
}
var increment = function(x){
return x+;
}
var incrementer = mapper(increment);
increment([,,]);//[2,3,4]
【squareofsum】
下面的函数接收两个函数f()和g(),并返回一个新函数用以计算f(g())
//返回一个新的可以计算f(g(...))的函数
//返回的函数h()将它所有的实参传入g(),然后将g()的返回值传入f()
//调用f()和g()时的this值和调用h()时的this值是同一个this
function compose(f,g){
return function(){
//需要给f()传入一个参数,所以使用f()的call()方法
//需要给g()传入很多参数,所以使用g()的apply()方法
return f.call(this,g.apply(this,arguments));
};
}
var square = function(x){
return x*x;
}
var sum = function(x,y){
return x + y;
}
var squareofsum = compose(square,sum);
squareofsum(,);//
上面代码中,首先执行compose(square,sum)。square传给f,sum传给g。然后执行f(g())。g作为f函数的参数,首先执行。即先执行sum(2,3),结果为5。再执行square(5),最终结果为25
最后
本文介绍了高阶函数的基础使用,主要包括参数传递和返回值输出两种形式。其中,高阶函数的一个重要应用是函数柯里化(currying),将在下篇博文中详细介绍
深入理解javascript函数进阶系列第一篇——高阶函数的更多相关文章
- 深入理解javascript选择器API系列第一篇——4种元素选择器
× 目录 [1]id属性 [2]标签名 [3]name属性[4]all 前面的话 说到最常见的DOM应用,恐怕就要数取得特定的某个或某组元素的引用了.DOM定义了许多方式来选取元素,包括getElem ...
- Python入门篇-高阶函数
Python入门篇-高阶函数 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.高级函数 1>.First Class Object 函数在Python中是一等公民 函数也 ...
- JavaScript学习笔记(十)——高阶函数之map,reduce,filter,sort
在学习廖雪峰前辈的JavaScript教程中,遇到了一些需要注意的点,因此作为学习笔记列出来,提醒自己注意! 如果大家有需要,欢迎访问前辈的博客https://www.liaoxuefeng.com/ ...
- javascript设计模式学习之三—闭包和高阶函数
一.闭包 闭包某种程度上就是函数的内部函数,可以引用外部函数的局部变量.当外部函数退出后,如果内部函数依旧能被访问到,那么内部函数所引用的外部函数的局部变量就也没有消失,该局部变量的生存周期就被延续. ...
- Python进阶:函数式编程(高阶函数,map,reduce,filter,sorted,返回函数,匿名函数,偏函数)...啊啊啊
函数式编程 函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计.函数就是面向过程的程序设计 ...
- (转)Python进阶:函数式编程(高阶函数,map,reduce,filter,sorted,返回函数,匿名函数,偏函数)
原文:https://www.cnblogs.com/chenwolong/p/reduce.html 函数式编程 函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数 ...
- Python序列函数、高级特性及高阶函数
序列函数: enumerate: for循环时记录索引,逐个返回元组(i, item) sorted:返回新的有序列表 zip:压缩将多个序列的对应位置的元素组成元组 zip(*元组列表): 解压缩 ...
- Python开发——函数【装饰器、高阶函数、函数嵌套、闭包】
装饰器 装饰器本质就是函数,为其他函数添加附加功能. 原则: 不修改被修饰函数的源代码 不修改被修饰函数的调用方法 装饰器知识储备:装饰器 = 高阶函数 + 函数嵌套 + 闭包 案例:求函数运行时间! ...
- pyhton 函数参数,递归函数,高阶函数(一点点笔记)
'''def test(x,y): print(x) print(y)test(2,y=3)def test(*args):#参数可以是不确定的多个数,接受N个位置参数,转换成元组形式 print(a ...
随机推荐
- 对Spring事务的一些误解
提起spring事务,就会让人联想起四大基本特征,五个隔离级别,七大传播特性.相信大多数人都知道这些东西,但是知道是一回事情,能用好真的是另一回事了.在使用Spring事务的时候,我曾遇到过几个比较严 ...
- JS框架设计读书笔记之-异步
setTimeout/setInterval 1. 如果回调执行时间大于间隔时间,真正的间隔时间会大一些. 2. 存在一个最小的时间间隔,即使seTimeout(fn,0),在IE6-IE8中大概为1 ...
- FastDFS 集群 安装 配置
这篇文章介绍如何搭建FastDFS 集群 FastDFS是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储.文件同步.文件访问(文件上传.文件下载)等,解决了大容量存储和负载均衡的 ...
- 关于防止sql注入的几个知识点
1.PDO预处理是首选. 你可以把他看成是要运行的sql的已经编译过的模板,它可以用变量参数进行定制 它有两个显著优点: 1.1:查询仅需解析一次,但可以用相同或者不同参数执行多次.换句话说如果要以不 ...
- HDU 3584 Cube(三位树状数组)
Cube Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others)Total Submi ...
- ajax请求发送和页面跳转的冲突
http://blog.csdn.net/allenliu6/article/details/77341538?locationNum=7&fps=1 在A页面中,有一个click事件,点击时 ...
- Python爬虫入门:爬虫基础了解
有粉丝私信我想让我出更基础一些的,我就把之前平台的copy下来了,可以粗略看一下,之后都会慢慢出. 1.什么是爬虫 爬虫,即网络爬虫,大家可以理解为在网络上爬行的一直蜘蛛,互联网就比作一张大网,而爬虫 ...
- 解决IE下CSS因 Mime 类型不匹配而被忽略的问题
写页面的时候在chrome,firefox等页面上显示正常,但是换成IE9之后就完全没有样式了,报错信息是CSS 因 Mime 类型不匹配而被忽略,下面与大家分享下这个问题的相关的回答.IE真是个奇葩 ...
- 30.Linux-RTC驱动分析及使用
linux中的rtc驱动位于drivers/rtc下,里面包含了许多开发平台的RTC驱动,我们这里是以S3C24xx为主,所以它的RTC驱动为rtc-s3c.c 1.进入./drivers/rtc/r ...
- Python之Queue模块
Queue 1.创建一个“队列”对象 >>> import Queue >>> queue = Queue.Queue(maxsize=100) >>& ...