快速概览

当我们说函数是“一等公民”的时候,我们实际上说的是它们和其他对象都一样…所以就是普通公民(坐经济舱的人?)。函数真没什么特殊的,你可以像对待任何其他数据类型一样对待它们——把它们存在数组里,当作参数传递,赋值给变量…等等。

这是 JavaScript 语言的基础概念,不过还是值得提一提的,因为在 Github 上随便一搜就能看到对这个概念的集体无视,或者也可能是无知。我们来看一个杜撰的例子:

  1. var hi = function(name){
  2. return "Hi " + name;
  3. };
  4. var greeting = function(name) {
  5. return hi(name);
  6. };

这里 greeting 指向的那个把 hi 包了一层的包裹函数完全是多余的。为什么?因为 JavaScript 的函数是可调用的,当 hi 后面紧跟 () 的时候就会运行并返回一个值;如果没有 ()hi 就简单地返回存到这个变量里的函数。我们来确认一下:

  1. hi;
  2. // function(name){
  3. // return "Hi " + name
  4. // }
  5. hi("jonas");
  6. // "Hi jonas"

greeting 只不过是转了个身然后以相同的参数调用了 hi 函数而已,因此我们可以这么写:

  1. var greeting = hi;
  2. greeting("times");
  3. // "Hi times"

换句话说,hi 已经是个接受一个参数的函数了,为何要再定义一个额外的包裹函数,而它仅仅是用这个相同的参数调用 hi?完全没有道理。这就像在大夏天里穿上你最厚的大衣,只是为了跟热空气过不去,然后吃上个冰棍。真是脱裤子放屁多此一举。

用一个函数把另一个函数包起来,目的仅仅是延迟执行,真的是非常糟糕的编程习惯。(稍后我将告诉你原因,跟可维护性密切相关。)

充分理解这个问题对读懂本书后面的内容至关重要,所以我们再来看几个例子。以下代码都来自 npm 上的模块包:

  1. // 太傻了
  2. var getServerStuff = function(callback){
  3. return ajaxCall(function(json){
  4. return callback(json);
  5. });
  6. };
  7. // 这才像样
  8. var getServerStuff = ajaxCall;

世界上到处都充斥着这样的垃圾 ajax 代码。以下是上述两种写法等价的原因:

  1. // 这行
  2. return ajaxCall(function(json){
  3. return callback(json);
  4. });
  5. // 等价于这行
  6. return ajaxCall(callback);
  7. // 那么,重构下 getServerStuff
  8. var getServerStuff = function(callback){
  9. return ajaxCall(callback);
  10. };
  11. // ...就等于
  12. var getServerStuff = ajaxCall; // <-- 看,没有括号哦

各位,以上才是写函数的正确方式。一会儿再告诉你为何我对此如此执着。

  1. var BlogController = (function() {
  2. var index = function(posts) {
  3. return Views.index(posts);
  4. };
  5. var show = function(post) {
  6. return Views.show(post);
  7. };
  8. var create = function(attrs) {
  9. return Db.create(attrs);
  10. };
  11. var update = function(post, attrs) {
  12. return Db.update(post, attrs);
  13. };
  14. var destroy = function(post) {
  15. return Db.destroy(post);
  16. };
  17. return {index: index, show: show, create: create, update: update, destroy: destroy};
  18. })();

这个可笑的控制器(controller)99% 的代码都是垃圾。我们可以把它重写成这样:

  1. var BlogController = {index: Views.index, show: Views.show, create: Db.create, update: Db.update, destroy: Db.destroy};

…或者直接全部删掉,因为它的作用仅仅就是把视图(Views)和数据库(Db)打包在一起而已。