最自由的一等公民

我觉得 JS 中的函数非常自由,这可能得益于 JS 的异步编程模型(见 Javascript Async)。

闭包

没有闭包的函数(当然也不考虑全局变量),它的输出只取决于函数参数;而有闭包的函数,它的输出不止取决于函数参数,还取决于闭包所捕获的环境。

其实全局变量就是一个很好的理解“环境”的例子,全局变量其实就是一种“环境”,只是它位于最上层。这是因为像 C 这种比较死板的语言,函数只能定义在 global 层,是不能在函数中定义函数的,所以函数的环境只是 global ,而如果可以在函数内部定义函数,那么内部函数的环境,就有可能是外部函数的局部变量了。这其实也是 Lexical Binding 中的内容。

那为什么一定要闭包而不是函数参数呢?两者的区别就是,前者是隐式的,而后者是显式的。难道不应该让显式代替隐式吗?我觉得如果从这个角度考虑的话,隐式最大的作用就是方便,我们不需要像只有显式参数的语言一样,动不动就传递一个 void *context ,比如说 Linux 开发中常用的 task_struct *t ,可能在嵌套调用中根本没有使用它,而是更深层次的函数需要,但是也需要传递。

灵活的函数参数

JavaScript 不要求调用函数时,传入的参数数量与声明的参数数量一致。这有两层意思:

第一,传入的参数可以比声明的参数少:

function foo(a, b) {
  console.log(a);
  console.log(b);
}
 
foo(1, 2); // 1, 2
foo(1);    // 1, undefined
foo();     // undefined, undefined

第二,传入的参数可以比声明的参数多:

function foo(a) {
  console.log(a);
}
 
foo(1, 2, 3); // 1

这种自由度,也是刻意为之的,它使得 callback 变得非常方便和灵活:

function A(callback) {
  callback(10, 20, 30);
}
 
A(a => {
  console.log(a); // 10
});