高阶函数

高阶函数是指至少满足下列条件之一的函数。
  (1)函数可以作为参数被传递
  (2)函数可以作为返回值输出

JavaScript 语言中的函数显然满足高阶函数的条件,在实际开发中,无论是将函数当作参数传递,还是让函数的执行结果返回另外一个函数,这两种情形都有很多应用场景,下面就列举一些高阶函数的应用场景。 

1.函数作为参数传递
把函数当作参数传递,这代表我们可以抽离出一部分容易变化的业务逻辑,把这部分业务逻辑放在函数参数中,这样一来可以分离业务代码中变化与不变的部分。其中一个重要应用场景就是常见的回调函数。
1. 回调函数
在 ajax 异步请求的应用中,回调函数的使用非常频繁。当我们想在 ajax 请求返回之后做一些事情,但又并不知道请求返回的确切时间时,最常见的方案就是把 callback 函数当作参数传入发起 ajax 请求的方法中,待请求完成之后执行 callback 函数:

var getUserInfo = function( userId, callback ){ 
 $.ajax( 'http://xxx.com/getUserInfo?' + userId, function( data ){ 
 if ( typeof callback === 'function' ){ 
 callback( data ); 
 } 
 }); 
} 
getUserInfo( 13157, function( data ){ 
 alert ( data.userName ); 
}); 

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

2.函数作为返回值输出
相比把函数当作参数传递,函数当作返回值输出的应用场景也许更多,也更能体现函数式编程的巧妙。让函数继续返回一个可执行的函数,意味着运算过程是可延续的。
1. 判断数据的类型
我们来看看这个例子,判断一个数据是否是数组,在以往的实现中,可以基于鸭子类型的概念来判断,比如判断这个数据有没有 length 属性,有没有 sort 方法或者 slice 方法等。但更好的方式是用 Object.prototype.toString 来计算。Object.prototype.toString.call( obj )返回一个字符串,比如 Object.prototype.toString.call( [1,2,3] ) 总是返回 "[object Array]" , 而
Object.prototype.toString.call( “str”)总是返回"[object String]"。所以我们可以编写一系列的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 )返回的字符串。为了避免多余的代码,我们尝试把这些字符串作为参数提前值入 isTyp函数。代码如下:

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 

3.高阶函数实现AOP
AOP(面向切面编程)的主要作用是把一些跟核心业务逻辑模块无关的功能抽离出来,这些跟业务逻辑无关的功能通常包括日志统计、安全控制、异常处理等。把这些功能抽离出来之后,再通过“动态织入”的方式掺入业务逻辑模块中。这样做的好处首先是可以保持业务逻辑模块的纯净和高内聚性,其次是可以很方便地复用日志统计等功能模块。在 Java 语言中,可以通过反射和动态代理机制来实现 AOP 技术。而在 JavaScript 这种动态语言中,AOP 的实现更加简单,这是 JavaScript 与生俱来的能力。通常,在 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 和打印数字 3 的两个函数通过 AOP 的方式动态植入 func 函数。

4.函数节流

JavaScript 中的函数大多数情况下都是由用户主动调用触发的,除非是函数本身的实现不合理,否则我们一般不会遇到跟性能相关的问题。但在一些少数情况下,函数的触发不是由用户直接控制的。在这些场景下,函数有可能被非常频繁地调用,而造成大的性能问题。

例如window.onresize 事件。我们给 window 对象绑定了 resize 事件,当浏览器窗口大小被拖动而改变的时候,这个事件触发的频率非常之高。如果我们在 window.onresize 事件函数里有一些跟 DOM 节点相关的操作,而跟 DOM 节点相关的操作往往是非常消耗性能的,这时候浏览器可能就会吃不消而造成卡顿现象。window.onresize 事件中要打印当前的浏览器窗口大小,在我们通过拖曳来改变窗口大小的时候,打印窗口大小的工作 1 秒钟进行了 10 次。而我们实际上只需要 2 次或者 3 次。这就需要我们按时间段来忽略掉一些事件请求,比如确保在 500ms 内只打印一次。很显然,我们可以借助 setTimeout 来完成这件事情。

关于函数节流的代码实现有许多种,下面的 throttle 函数的原理是,将即将被执行的函数用setTimeout 延迟一段时间执行。如果该次延迟执行还没有完成,则忽略接下来调用该函数的请求。throttle 函数接受 2 个参数,第一个参数为需要被延迟执行的函数,第二个参数为延迟执行的时间。具体实现代码如下:

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);

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值