高阶函数的应用
函数作为参数传递
- 回调函数
let getUserInfo = function ( userId, callback ) {
$ajax('http://xxx.com/getUserInfo?id=userId', function (data){
if(typeof callback==='function'){
callback(data)
}
})
}
-
Array.prototype.sort
// 从小到大 [1,4,3]sort( function ( a , b ) { return a - b; }); // 输出 [1,3,4] // 从大到小 [1,4,3]sort( function ( a , b ) { return b - a; }); // 输出 [4,3,1]函数作为返回值输出
-
判断数据的类型
let Type = {}; for (let 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([]); Type.isString( "S" ) -
getSingle
// 这只是单例模式的例子 let getSingle = function ( fn ) { let ret; return function () { return ret || ( ret = fn.apply( this , arguments ) ) }; }; // 此函数既把函数当作参数传递,又让函数执行后返回了另外一个函数。下面是执行效果 let getScript = getSingle(function(){ return document.creatElement( 'script' ); }); let script1 = getScript(); let script2 = getScript(); alert(script1 === script2); // 输出 true
高阶函数实现AOP
AOP的主要作用是把一些跟核心业务逻辑模块无关的功能抽离出来,这些跟业务逻辑无关的功能通常包括日志统计,安全控制,异常处理等。把这些功能抽离出来后,再通过“动态织入”的方式掺入业务逻辑中。这样做的好处首先是可以保持业务逻辑模块的纯净和高内聚性,其次是可以很方便的复用日志统计等功能模块。
在JavaScript中实现AOP,都是指把一个函数“动态织入”到另外一个函数之中,具体的技术很多。本例通过扩展Function.prototype来做到这点。
Function.prototype.before = function( beforefn ){
var __self = this; // 保存原函数的引用
return function(){ // 返回包含了原函数和新函数的"代理"函数
beforefn.apply( this, arguments ); // 执行新函数,修正this
return __self.apply( this, arguments ); // 执行原函数
}
};
Function.prototype.after = function( afterfn ){
var __self = this;
return function(){
var ret = __self.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
// 2
// 3
高阶函数的其他应用
-
curring curring又称
部分求值。一个curring函数首先会接受一些参数,接受参数之后并不会立即执求值,二是继续返回另外一个函数,刚才传入的参数在函数形成的 闭包中保存起来。待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性用于求值。 例子:// 计算每个月开销 var monthlyCost = 0; var cost = function( money ){ monthlyCost += money; };
cost( 100 ); // 第1 天开销 cost( 200 ); // 第2 天开销 cost( 300 ); // 第3 天开销 //cost( 700 ); // 第30 天开销 alert ( monthlyCost ); // 输出:600
我们可以看到每天花掉的钱,但是我们的目的是到月底看到一共花了多少钱,所以只需要月底计算一次。
var cost = (function(){
var args = [];
return function(){
if ( arguments.length === 0 ){
var money = 0;
for ( var i = 0, l = args.length; i < l; i++ ){
money += args[ i ];
}
return money;
}else{
[].push.apply( args, arguments );
}
}
})();
cost( 100 ); // 未真正求值
cost( 200 ); // 未真正求值
cost( 300 ); // 未真正求值
console.log( cost() ); // 求值并输出:600
但这还不是完整的curring。接下来我们写一个通用的function curring(){},接受一个参数,即将要被curring的函数。这个例子里,这个函数的作用是遍历本月每天的开销并求出她们的总和。代码如下:
var currying = function( fn ){
var args = [];
return function(){
if ( arguments.length === 0 ){
return fn.apply( this, args );
}else{
[].push.apply( args, arguments );
return arguments.callee;
}
}
};
var cost = (function(){
var money = 0;
return function(){
for ( var i = 0, l = arguments.length; i < l; i++ ){
money += arguments[ i ];
}
return money;
}
})();
var cost = currying( cost ); // 转化成currying 函数
cost( 100 ); // 未真正求值
cost( 200 ); // 未真正求值
cost( 300 ); // 未真正求值
alert ( cost() ); // 求值并输出:600
-
函数节流 原理:将即将被执行的函数用setTimeout延迟执行。如果该次延迟执行还没有完成,那么则忽略接下来调用该函数的请求。throttle函数接收两个参数,第一个参数为需要被延迟执行的函数,第二个为延迟执行的时间。具体代码:
var throttle = function ( fn, interval ) { var __self = fn, // 保存需要被延迟执行的函数引用 timer, // 定时器 firstTime = true; // 是否是第一次调用 return function () { var args = arguments, __me = this; if ( firstTime ) { // 如果是第一次调用,不需延迟执行 __self.apply(__me, args); return firstTime = false; } if ( timer ) { // 如果定时器还在,说明前一次延迟执行还没有完成 return false;
timer = setTimeout(function () { // 延迟一段时间执行 clearTimeout(timer); timer = null; __self.apply(__me, args); }, interval || 500 ); };};
window.onresize = throttle(function(){ console.log( 1 ); }, 500 );