函数定义

在 JavaScript 中,定义函数有两种方式:

  1. 传统函数:使用 function 关键字定义函数
  2. 箭头函数

举个例子:

  1. function add1(a, b) {
  2. return a + b;
  3. }
  4. let add2 = function(a, b) {
  5. return a + b;
  6. }
  7. let add3 = (a, b) => {
  8. return a + b;
  9. }
  10. let add4 = (a, b) => a + b;
  11. console.log(add1(1, 2)); // 打印 3
  12. console.log(add2(1, 2)); // 打印 3
  13. console.log(add3(1, 2)); // 打印 3
  14. console.log(add4(1, 2)); // 打印 3

上面三个 add 函数在功能上完全一样(都是把两个参数相加),使用方式也一样。其中:

  • add1 是传统函数,用 function 关键字定义的函数。这个函数的名字叫 add1
  • add2 是一个变量,其值是一个没有名字的函数。我们把没有名字的函数叫匿名函数
  • add3 也是一个变量,其值是一个箭头函数。可以看到箭头函数没有名字,所以箭头函数式匿名函数;
  • add4 也是一个变量,其值是一个简化版本的箭头函数。如果箭头函数体可以简化为 return 一个表达式,那么就可以把 return 关键字和外面的花括号一并省略。这种形式叫 lambda 表达式。

箭头函数和传统函数,除了写法上的区别之外,还有一些隐晦的区别。这里我们不关注这些区别,并且在下文中,倾向于使用箭头函数。想了解箭头函数和传统函数之间到底有哪些区别的话,可以参考这里:MDN-箭头函数

例子中的 add2add3add4事实上是变量,这里体现了函数作为值得一面。

函数作为值

可以把函数作为值直接传递到变量上。比如:

  1. let pi = 3.1415926546; // 用数字初始化变量 pi
  2. let isEven = (num) => num % 2 == 0; // 用函数初始化变量

函数还可以用做函数的参数。比如:

  1. const calc = (a, b, binaryOperateFunction) => {
  2. return binaryOperateFunction(a, b)
  3. }
  4. const add = (a, b) => a + b;
  5. const multiply = (a, b) => a * b;
  6. console.log(calc(2, 3, add)) // 输出 5,即 2 + 3
  7. console.log(calc(2, 3, multiply)) // 输出 6,即 2 * 3
  8. console.log(calc(2, 3, Math.pow)) // 输出 8,即 2 ^ 3

函数还可以用做函数的返回值。比如:

  1. const add = (a) => {
  2. return (b) => a + b;
  3. }
  4. // 可以简写做 const add = (a) => (b) => a + b;
  5. const tenAdd = add(10)
  6. console.log(tenAdd(2)) // 输出 12
  7. console.log(tenAdd(7)) // 输出 17
  8. console.log(add(2)(3)) // 输出 5

我们观察发现上面的tenAddadd函数的一个返回值。在使用tenAdd的过程中,10好像被以某种方式存储到tenAdd中了。我们说10被“焙入”(bake in)tenAdd了。

用函数作为参数或者返回值得函数被称为高阶函数(high-order function)。

递归函数

粗略地说,递归函数就是自己调用自己的函数。举一个阶乘的例子。
阶乘的数学定义是这样的:
从函数说起 - 图1
这个定义是递归的。我们可以直接把它翻译成 JavaScript 代码:

  1. const f = (n) => {
  2. if (n == 1) return 1;
  3. else return n * f(n-1);
  4. }
  5. // 或者直接写作: const f = (n) => n == 1 ? 1 : n * f(n-1);

有了高阶函数和递归函数,我们就可以做一些奇怪的事了。