深入理解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( 123, function( data ){ alert ( data.userName );
});

  回调函数的应用不仅只在异步请求中,当一个函数不适合执行一些请求时,也可以把这些请求封装成一个函数,并把它作为参数传递给另外一个函数,“委托”给另外一个函数来执行

  比如,想在页面中创建100个div节点,然后把这些div节点都设置为隐藏。下面是一种编写代码的方式:

var appendDiv = function(){for ( var i = 0; i < 100; 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 = 0; i < 100; 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 ]
[ 1, 4, 3 ].sort( function( a, b ){ return a - b;
});// 从大到小排列,输出: [ 4, 3, 1 ]
[ 1, 4, 3 ].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( [ 1, 2, 3 ] ) );    // 输出:true

  当然,还可以用循环语句,来批量注册这些 isType 函数:

var Type = {};
for ( var i = 0, 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(2);};func = func.before(function () {console.log(1);}).after(function () {console.log(3);});func();

  把负责打印数字1和打印数字3的两个函数通过AOP的方式动态植入func函数。通过执行上面的代码,控制台顺利地返回了执行结果1、2、3

//1
//2
//3

 

其他应用

【not】

  下面的not函数用于返回参数的返回值的逻辑非

  function not(f) {return function () {return !(f.apply(this, arguments));};}//偶数时,返回true;奇数时,返回falsevar even = function (x) {return x % 2 === 0;}//偶数时,返回false;奇数时,返回truevar odd = not(even);[1, 1, 3, 5, 5].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+1;
}
var incrementer = mapper(increment);
increment([1,2,3]);//[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(2,3);//25

  上面代码中,首先执行compose(square,sum)。square传给f,sum传给g。然后执行f(g())。g作为f函数的参数,首先执行。即先执行sum(2,3),结果为5。再执行square(5),最终结果为25

 

最后

  本文介绍了高阶函数的基础使用,主要包括参数传递和返回值输出两种形式。其中,高阶函数的一个重要应用是函数柯里化(currying),将在下篇博文中详细介绍

 

转载于:https://www.cnblogs.com/xiaohuochai/p/8026038.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/256029.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ANSYS WORKBENCH——参数化建模以及参数优化(结果导出为Excel)

目录 1、打开软件workbench 2、找到static structure,双击打开 3、选择材料 4、参数化建模 ​

centos 安装软件

1&#xff09;一种是软件的源代码&#xff0c;您需要自己动手编译它。这种软件安装包通常是用gzip压缩过的tar包&#xff08;后缀为.tar.gz&#xff09;。2&#xff09;另一种是软件的可执行程序&#xff0c;你只要安装它就可以了。这种软件安装包通常被是一个RPM包&#xff08…

【图像处理】——傅里叶变换、DFT以及在图像上的应用

目录 1、傅里叶变换 2、DFT 1)一维离散傅里叶变换: 离散傅里叶变换例子

JAVA开发Web Service几种框架介绍

下面分别介绍一个这几种Web Service框架的基本概念 1、JWS是Java语言对WebService服务的一种实现&#xff0c;用来开发和发布服务。而从服务本身的角度来看JWS服务是没有语言界限的。但是Java语言为Java开发者提供便捷发布和调用WebService服务的一种途径。 2、Axis2是Apache下…

基于CMake构建MSVC_CUDA及MinGW编译环境下的的OpenCV项目

前言 第一次搭建OpenCV开发环境的时候各种报错&#xff0c;内心那个烦啊&#xff0c;简直了。当时只能针对某个特定的错误去寻找特定的解决方法&#xff0c;在OpenCV构建过程中出现最多的问题就是各个模块文件的下载问题&#xff0c;本质上这类问题的解决思路都是一样的&#…

OC Autorelease

implementation ViewController - (void)viewDidLoad {[super viewDidLoad];__unsafe_unretained NSObject *obj1 [ViewController getObj];NSLog("%",obj1); // 运行OK__unsafe_unretained NSObject *obj2 [ViewController getObj];NSLog("%",obj2); //…

【opencv】——钢管计数(霍夫圆变换 + 阈值 + canny)

目录 方法一:霍夫圆变换 + canny 方法二 阈值 + 寻边 对图中的钢管进行计数 方法一:霍夫圆变换 + canny

svn服务器搭建-SuSE Linux Enterprise Server 11 SP3

svn存储版本数据也有2种方式&#xff1a;1.bdb&#xff1b;2.fsfs。因为BDB方式在服务器中断时&#xff0c;有可能锁住数据&#xff08;搞ldap时就深受其害&#xff0c;没法根治&#xff09;&#xff0c;所以还是FSFS方式更安全一点&#xff0c;我也选择这种方式。下载相关软件…

Swift 2.0初探:值得注意的新特性

转眼间&#xff0c;Swift已经一岁多了&#xff0c;这门新鲜、语法时尚、类型安全、执行速度更快的语言已经渐渐的深入广大开发者的心。我同样也是非常喜爱这门新的编程语言。 今年6月&#xff0c;一年一度的WWDC大会如期而至&#xff0c;在大会上Apple发布了Swift 2.0&#xff…

Android 自定义WebView弹窗及屏蔽弹窗

额&#xff0c;还是那个WebView的问题&#xff0c;内核已换成腾讯X5内核&#xff0c;所以接下来的内容会有一些X5内核的方法。但我们的H5是不能改的&#xff0c;还是只有委屈我们自己。先看看H5自带的弹窗 这样子的弹窗在不同的手机上呈现的可能是不同的效果&#xff0c;效果不…

【图像处理】——Python实现two_pass方法来进行连通域的提取

目录 一、相关知识 1、two_pass算法思想 2、并查集算法 二、自定义的two_pass算法

C++ 多线程使用future传递异常

如果 std::async 调用的函数抛出异常&#xff0c;那么这个异常会被存储在值的位置&#xff0c;同时 future 变为 ready ,如果调用 get() 会重新抛出存储的异常。 Note: 标准并没有指定原来的异常对象是被重新抛出或者拷贝后抛出&#xff0c;不同的编译器会做不同的选择。 对于 …

期货黄金与现货黄金比较

现货黄金与期货黄金是目前市场上最热门的黄金投资方式&#xff0c;与国内任何的金融投资品相比&#xff0c;都具有一定的优势。 其实金投网小编觉得现货黄金与期货黄金最主要的不同点是这个&#xff1a;期货黄金做的是国内市场&#xff0c;同股票市场一样&#xff0c;里面有庄家…

DNS域传送漏洞

0x00 相关背景介绍 Dns是整个互联网公司业务的基础&#xff0c;目前越来越多的互联网公司开始自己搭建DNS服务器做解析服务&#xff0c;同时由于DNS服务是基础性服务非常重要&#xff0c;因此很多公司会对DNS服务器进行主备配置而DNS主备之间的数据同步就会用到dns域传送&#…

封装之--通过类中公有方法访问私有成员变量

如何在ClassB中访问ClassA的私有成员变量&#xff1f;&#xff08;典型的封装案例&#xff09; 通过在ClassA中定义公有的成员方法&#xff0c;然后&#xff0c;在ClassB中通过ClassA的对象调用ClassA中的公有方法&#xff0c;来访问ClassA中的私有成员变量。 转载于:https://w…

匹配物镜放大倍数与相机像元尺寸

通常来说&#xff0c;相机内部的CCD或者CMOS传感器上都有感光阵列&#xff0c;由一个一个的感光元件构成&#xff0c;每一个感光元件负责完成光电转换的过程。简单理解&#xff0c;一个感光元件可以认为就是一个像素(pixel)或像元(pel)。像元具有一定尺寸&#xff0c;如果像的尺…

2016/11/10 kettle概述

ETL(Extract-Transform-Load&#xff0c;即抽取&#xff0c;转换&#xff0c;加载)&#xff0c;数据仓库技术&#xff0c;是用来处理将数据从来源&#xff08;以前做的项目&#xff09;经过抽取&#xff0c;转换&#xff0c;加载到达目的端&#xff08;正在做的项目&#xff09…

【深度学习】——非极大值抑制(nms/soft-nms)

目录 一、相关概念 1、iou 1&#xff09;理论计算 2&#xff09;Python代码&#xff08;代码参考yolov3模型util.py文件&#xff09; 2、nms 1)基本思路 2&#xff09;标准nms和soft-nms 3&#xff09;Python代码实现&#xff08;yolov3中util.py文件&#xff0c;增加了…

移动服务安全现状分析!

2019独角兽企业重金招聘Python工程师标准>>> 由于Android开源的环境&#xff0c;导致Android的整体环境都存在很多不安全的因素&#xff0c;同时用户在移动APP客户端的便捷应用&#xff0c;也给用户带来了巨大的安全隐患。未经过移动服务安全加固的APP存在被静态反编…

封装不同类模板的随机数生成器

最近准备刷题&#xff0c;打算简单封装下随机数生成器&#xff0c;方便产生测试数据。C11的STL提供了很多分布类型&#xff0c;我比较常用的是均匀分布&#xff0c;均匀分布的值有两种类型&#xff0c;一类是整数&#xff0c;另一类是浮点数&#xff0c;STL根据值的类型定义了两…