函数定义
在 JavaScript 中,定义函数有两种方式:
- 传统函数:使用
function关键字定义函数 - 箭头函数
举个例子:
function add1(a, b) {return a + b;}let add2 = function(a, b) {return a + b;}let add3 = (a, b) => {return a + b;}let add4 = (a, b) => a + b;console.log(add1(1, 2)); // 打印 3console.log(add2(1, 2)); // 打印 3console.log(add3(1, 2)); // 打印 3console.log(add4(1, 2)); // 打印 3
上面三个 add 函数在功能上完全一样(都是把两个参数相加),使用方式也一样。其中:
add1是传统函数,用function关键字定义的函数。这个函数的名字叫add1;add2是一个变量,其值是一个没有名字的函数。我们把没有名字的函数叫匿名函数;add3也是一个变量,其值是一个箭头函数。可以看到箭头函数没有名字,所以箭头函数式匿名函数;add4也是一个变量,其值是一个简化版本的箭头函数。如果箭头函数体可以简化为return一个表达式,那么就可以把return关键字和外面的花括号一并省略。这种形式叫 lambda 表达式。
箭头函数和传统函数,除了写法上的区别之外,还有一些隐晦的区别。这里我们不关注这些区别,并且在下文中,倾向于使用箭头函数。想了解箭头函数和传统函数之间到底有哪些区别的话,可以参考这里:MDN-箭头函数。
例子中的 add2、add3和add4事实上是变量,这里体现了函数作为值得一面。
函数作为值
可以把函数作为值直接传递到变量上。比如:
let pi = 3.1415926546; // 用数字初始化变量 pilet isEven = (num) => num % 2 == 0; // 用函数初始化变量
函数还可以用做函数的参数。比如:
const calc = (a, b, binaryOperateFunction) => {return binaryOperateFunction(a, b)}const add = (a, b) => a + b;const multiply = (a, b) => a * b;console.log(calc(2, 3, add)) // 输出 5,即 2 + 3console.log(calc(2, 3, multiply)) // 输出 6,即 2 * 3console.log(calc(2, 3, Math.pow)) // 输出 8,即 2 ^ 3
函数还可以用做函数的返回值。比如:
const add = (a) => {return (b) => a + b;}// 可以简写做 const add = (a) => (b) => a + b;const tenAdd = add(10)console.log(tenAdd(2)) // 输出 12console.log(tenAdd(7)) // 输出 17console.log(add(2)(3)) // 输出 5
我们观察发现上面的tenAdd是add函数的一个返回值。在使用tenAdd的过程中,10好像被以某种方式存储到tenAdd中了。我们说10被“焙入”(bake in)tenAdd了。
用函数作为参数或者返回值得函数被称为高阶函数(high-order function)。
递归函数
粗略地说,递归函数就是自己调用自己的函数。举一个阶乘的例子。
阶乘的数学定义是这样的:
这个定义是递归的。我们可以直接把它翻译成 JavaScript 代码:
const f = (n) => {if (n == 1) return 1;else return n * f(n-1);}// 或者直接写作: const f = (n) => n == 1 ? 1 : n * f(n-1);
有了高阶函数和递归函数,我们就可以做一些奇怪的事了。
