在一般的编程语言中,函数的参数只能是基本类型或者对象引用,返回值也只是基本数据类型或对象引用。但在Javascript中函数做为一等公民,既能够当作参数传递,也能够被当作返回值返回。所谓高阶函数就是能够把函数做为参数,或者是将函数做为返回值的函数。这两种情形在实际开发中有不少应用场景,本文是我在工做学习中遇到的几种应用场景的总结。node
回调函数编程
代码复用是衡量一个应用程序的重要标准之一。经过将变化的业务逻辑抽离封装在回调函数中可以有效的提升代码复用率。好比ES5中为数组增长的forEach方法,遍历数组,对每一个元素调用同一个函数。设计模式
array = {}; array.forEach = function(arr, fn){ for (var i = 0, len = arr.length; i < len; i++) { fn(arr[i], i, arr); } }
经过回调函数将业务的重点聚焦在回调函数中,而没必要每次都要重复编写遍历代码。数组
偏函数浏览器
做为将函数当作返回值输出的典型应用就是偏函数。所谓偏函数是指建立一个调用另一个部分——参数或变量已经预置的函数——的函数的用法。反正看着定义我是没理解这东东干吗的。我们仍是先看例子吧,偏函数最典型的例子就是类型判断。闭包
Javascript对象都拥有三个属性:原型属性、类属性、可扩展性。(不知道的同窗要回去翻犀牛书哦,page:138)类属性是一个字符串,Javascript中并未直接提供,但咱们能够利用Object.prototype.toString来间接获得。该函数老是返回以下形式:app
[object Class]
所以咱们能够编写一系列isType函数。代码以下:dom
isString = function(obj){ return Object.prototype.toString.call(obj) === "[object String]"; } isNumber = function(obj){ return Object.prototype.toString.call(obj) === "[object Number]"; } isArray = function(obj){ return Object.prototype.toString.call(obj) === "[object Array]"; }
这几个函数中大部分代码是重复的,这时高阶函数便华丽丽的登场了:编程语言
isType = function(type) { return function(obj) { return Object.prototype.toString.call(obj) === "[object " + type + "]"; } } isString = isType('String'); isNumber = isType('Number'); isArray = isType('Array');
因此经过指定部分参数来返回一个新的定制函数的形式就是偏函数。函数
currying(柯里化)
currying又称部分求值。一个currying的函数首先会接受一些参数,接受这些参数以后,函数并不会当即求值,而是继续返回另外一个函数,刚才传入的参数在函数造成的闭包中被保存起来。待到函数被真正须要求值的时候,以前传入的全部参数都会被一次性用于求值。
var currying = function(fn) { var args = []; return function() { if (arguments.length === 0) { return fn.applay(this, args); } else { args = args.concat(arguments); return arguments.callee; } } }
假设咱们以计算一个月天天花销为例:
var currying = function(fn) { debugger; var args = []; return function() { if (arguments.length === 0) { return fn.apply(this, args); } else { Array.prototype.push.apply(args, arguments); return arguments.callee; } } } cost = function(){ var sum = 0; for (var i = 0, len = arguments.length; i < len; i++) { sum += arguments[i]; } return sum; } var cost = currying(cost); cost(100); cost(200); alert(cost())
事件节流
在某些场景下,某些事件可能会被重复的触发,但事件处理函数并不须要每次都执行。好比在window.resize事件中进行复杂的逻辑计算,若是用户频繁的改变浏览器大小,复杂计算会对性能形成严重影响;有时这些逻辑计算并不须要每次rezise时都触发,只须要计算有限的几回即可以。这时咱们须要根据时间段来忽略一些事件请求。请看如下节流函数:
function throttle(fn, interval) { var doing = false; return function() { if (doing) { return; } doing = true; fn.apply(this, arguments); setTimeout(function() { doing = false; }, interval); } } window.onresize = throttle(function(){ console.log('execute'); }, 500);
经过控制函数执行时间,能够在函数执行次数与功能需求之间达到完美平衡。另外一个事件是mousemove。若是咱们给一个dom元素绑定该事件,鼠标在改元素上移动时,该事件便会重复触发。
事件结束
对于某些能够频繁触发的事件,有时候咱们但愿在事件结束后进行一系列操做。这时咱们能够利用高阶函数作以下处理:
function debounce(fn, interval) { var timer = null; function delay() { var target = this; var args = arguments; return setTimeout(function(){ fn.apply(target, args); }, interval); } return function() { if (timer) { clearTimeout(timer); } timer = delay.apply(this, arguments); } }; window.onresize = throttle(function(){ console.log('resize end'); }, 500);
若是在这一过程当中事件被触发则清除上一次事件句柄,从新绑定执行时间。
参考资料:
《深刻浅出node》
《Javascript设计模式与开发实践》