函数和变量是一个东西?

Javascript是一种函数式语言。所谓函数式语句,即是说函数跟变量一样。

除了平常定义函数的方式外,我们也可以像定义变量那样去定义一个函数:

1
2
3
var ascending = function(a, b) {
  return a - b;
};

既然是变量,那么它也可以作为传入值。比如说 Array.sort 函数。可以接收一个 Function 作为排序的依据。

1
2
var numbers = [49, 1, 3, 2, 59];
numbers.sort(ascending);

当我们传入不同的 Function ,那么输出的排序方式也是不一样的:

1
2
3
4
var descending = function(a, b) {
  return b - a;
}
numbers.sort(descending);

所以,基本上函数和变量是一样的东西,有着一样的行为和使用方式。而这种把函数作为变量使用的方式即为高阶函数。

高阶函数为我们编写程序时提供极大的灵活性,使用高阶函数我们在编程时的思维方式也产生了变化。利用高阶函数的特点,我们可以把业务逻辑放到函数里面传到另外的函数中由它决定何时调用我们封装的逻辑。

在典型的 Ajax 应用中,如 jQuery.ajax , 我们传入的 success 之类的回调函数,具体什么时间会被调用是无法确定的,但其实我们并不需要知道具体调用的时间,而是它被调用的时机。依赖于回调机制,我们知道 success 是在顺利加载远程资源后被触发,所以封闭的逻辑只要处理相应的资源就行了。这里的行为方式跟传统的顺序执行不太一样,比如 jQuery.ajax 之后的代码往往先于 success 的代码被执行。开始的时候可能会不太适应,但习惯之后,这样的异步回调机制极为高效。Node.js 基于这样的理念配合标准的异步I/O库产生的巨大并发处理能力是有目共睹的。相比于 C# 的 Task 系统和 lambda (其实我现在还不太理解 await async 这些关键字到底是怎么协作的!task 反而好理解些),反而高阶函数是更容易掌握及理解的技巧,同时代码也精简很多。。

函数还可以利用程序生成?

既然函数可以像变量那样定义,使用,赋值,作为返回值。那它当然可以生成了。

1
2
3
4
5
6
7
var foo = function() {
  return function() {
    return "Hello World";
  };
};
var bar = foo();
bar(); // return "Hello world";

当然,纯粹只返回一个固定作用的函数是没有意义的。往往我们需要一个函数返回一个新的函数是因为我们需要它们具有不同的行为结果。假如有时候我需要函数说的是 Fuck World 呢?

1
2
3
4
5
6
var foo = function(x) {
  var y = "World";
  return function() {
    return x + " " + y;
  }
}
1
2
3
4
var fuckBar = foo("Fuck");
fuckBar(); // return "Fuck World";
var helloBar = foo("Hello");
helloBar(); // return "Hello World";

这种通过输入不同的参数产生行为不一样的新函数即为偏函数。

这里有一个闭包的概念在里面, 当 bar 离开 foo 作为独立的存在时,bar 是可以访问甚至修改它还在 foo 里面所能看到的变量。 在上面的例子中,bar 可以访问到 x 和 y,即使它离开了foo之后!并且每次返回来的 bar 它们的变量都是独立存在的。每个 bar 都是在单独的作用域中独立产生的。两个 bar 能独立正常地工作,说明它们各自能访问到属于自己的闭包变量。当然,能访问到的也能修改。这个高阶函数的行为有点像OO语言中的实例,不同的是定义更为简短,方式也更为灵活。在起到数据保护的作用之余,也避免了全局变量污染的问题。

新的 asp.net mvc 基本上采用很多类似的理念设计,看 mvc 的出错页可以发现无论有没有给 controller 加上 async 关键字,后台实现实际上是有 Task 包装的,从而实现异步并发。根据MS的消息下一版 mvc 即 asp.net 5(原vnext) 将会采用 OWIN 风格设计,这玩意怎么看都是对 Node.js connect 模式的 copy。再配合 Roslyn 实时编译引擎,那么届时的 mvc Programming体验岂非很接近于 Node.js Express 了。要知道每次改点代码都要重新 build and run 也是相当花时间的。