javascript高阶函数

javascript高阶函数

高阶函数

这是一个有趣的东西,也说明了javascript对象的强大,我们要做的就是输出一个helloworld。

高阶看上去就像是一种先进的编程技术的一个深奥术语【ps:我刚开始是这样子认为的。】

然而,在javascript中,高阶函数只是将函数作为参数或返回值的函数。我们来看一个helloworld的简单例子。

 var test = function(p1){

    this.add = function (p2){

        return p1 + ' ' + p2;
    };
    return add;
};

我们可以这样子使用这个函数:

console.log(test('Hello')('World'));

上面的这个过程我们可以简写一下就会更加明白,实际上test(‘Hello’)是一个函数。

var t=test('Hello');
t('World');
"Hello World"

从上面我们可以看出,高阶函数可以使代码更加的简洁、高效。

接下来来一个简单实例

add = function(a,b){
    return a + b;
};
function math(func,array){
    return func(array[0],array[1]);
}
console.log(math(add,[1,2]));

在上面的实例中,传进去的add是一个参数,返回的时候是一个函数。

接下来,看一下应用场景

1 回调函数

通过回调函数将业务的重点聚焦在回调函数中,而不必每次都要重复编写遍历代码。
例如ajax请求

2 偏函数

作为将函数当作返回值输出的典型应用就是偏函数。看个类型判断的例子。

js的对象有三个属性:原型、类、可扩展性。判断类型的代码如下

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

所以通过指定部分参数来返回一个新的定制函数的形式就是偏函数。

3 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())

4 事件节流

  在某些场景下,某些事件可能会被重复的触发,但事件处理函数并不需要每次都执行。比如在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元素绑定该事件,鼠标在改元素上移动时,该事件便会重复触发

5 事件结束

  对于某些可以频繁触发的事件,有时候我们希望在事件结束后进行一系列操作。这时我们可以利用高阶函数做如下处理:

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

如果在这一过程中事件被触发则清除上一次事件句柄,重新绑定执行时间