在学习 JavaScript 的函数式编程的过程中遇到了一个高阶函数的概念,于是对其进行了一翻搜索,找到了一些相关的内容,特在这里进行一下梳理记录,以便理解。

什么是高阶函数

高阶函数的概念来自数学,维基百科中对它有如下定义:

在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数:

  • 接受一个或多个函数作为输入
  • 输出一个函数

具体来说,在 JavaScript 中,我们可以将一个函数 A 作为参数传给另一个函数 B,或者,在函数 B 中将函数 A 作为返回值返回。那么这里的函数 B 就是上面所说的高阶函数。

举例

这种应用场景在我们日常的编码中会经常见到,只不过我们可能不知道它叫高阶函数。在 JavaScript 中,将函数作为参数的例子比比皆是,如数组的 forEach 函数就是一个高阶函数:

[1, 2, 3].forEach(function (item) {
    console.log(item);
});

再来举一个将函数作为返回值的例子,最常见的就是 Function.prototype.bind 函数:

var person = {
  name: 'hello, world',
  getName: function () {
    return this.name;
  }
};

var getName = person.getName.bind(person);

getName(); // 'hello, world'

高级应用

在 JavaScript 的函数式编程中,我们可以通过灵活的使用高阶函数来实现函数的组合 (Composition)。

比如有如下代码:

var person = {
  setName: function (name) {
    this.name = name;
  },
  sayName: function () {
    alert(this.name);
  }
};

person.setName('Kevin Yue');
person.sayName(); // Kevin Yue

假如我们想要实现像 jQuery 中那样可以链式调用的方式就更好了,为了实现这个功能,我们需要在每个方法的结尾处添加一句 return this 的代码。但我们也可以在此处借助一个函数来实现这个功能,面不用修改具体方法的内部逻辑。例如,我们可以定义一个名为 chainable 的函数:

function chainable(fn) {
  return function () {
    fn.apply(this, arguments);
    return this;
  };
}

chainable 这个函数中,我们将一个函数作为参数传入,并返回一个新的函数,在这个新函数中我们会调用之前传入的函数,并返回了 this。我们可以将之前的代码作相应的修改:

var person = {
  setName: chainable(function (name) {
    this.name = name;
  }),
  sayName: chainable(function () {
    alert(this.name);
  })
};

person.setName('Kevin Yue').sayName(); // Kevin Yue

在上面的代码中,我们没有对之前的 setNamesayName 的逻辑作任何的修改,只是给它包裹了一层 chainable 函数,就可以实现链式调用了,类似的,我们还可以把 chainable 应用到其他需要链式调用的方法上。

装饰器

像上面这种方式,我们给函数添加一层包裹函数,用来修改增强扩展原函数的默认行为,这层包裹函数就可以称为一个装饰器。这也是 JS 中装饰器模型的一种实现。在 ES2016/ES7 中,JavaScript 在语言层面对这种模型进行了定义和封装,使得调用更加方便。这便是 ES7 中引入的装饰器,我们将在后续的文章中进行探讨。

小结

函数是 JS 的精髓所在,高阶是函数式编程中的一个重要的概念,二者结合,灵活使用,可以在一定程度上提高我们的编码水平。