詹臻臻的主页

我生于1982年,小时候对一切自己会动的玩具感兴趣。1989年读小学1年级时,家里有了一台叫做“中华学习机”的电脑,从此接触并爱上编程(因为觉得会自动运行真的很好玩)。这幅照片显示了我小时候是怎样玩电脑的:

那时没有专门的显示器,这台黑白电视机勉强能够应付,里面可以看到似乎有文字,其实是一段BASIC程序。这电脑是兼容Apple-II的,还接了个5寸盘的驱动器(在左边)。

而到了今天,人们面临着众多选择时,我的选择又是什么呢?下面详细地介绍一下。

目录 其他文章
比特币科普及其未来三种结局

哪种语言?

2010年之前,我和许多人一样,选择跟随微软,觉得C#,特别是函数式的F#前景光明。但后来发觉,最有前途的语言其实是JavaScript,或者说是它的变异体CoffeeScript。

首先是因为未来的电脑、手机,以及可穿戴智能设备中,非浏览器类应用终将逐渐凋零。为什么?因为App框架是一种“私有”的标准,每个大公司如微软、苹果都只希望自己的框架取胜。但唯有浏览器的语言,是统一的。私有的平台赢得暂时的胜利,但统一的平台将赢得最终的胜利,只要它不断进步。

其次是因为服务端JavaScript,即Node.js的出现,使程序员“只用一种语言”的理想得到了实现。

最后是因为JavaScript本身是函数式的(虽然比较丑),这符合近些年流行的趋势,而CoffeeScript用更简洁的风格改良了JavaScript,使它的函数式特征一下子美丽地表现出来了。

所以,如果你是一个完美主义者,那么请用CoffeeScript,它和JavaScript完全兼容。

用什么电脑开发?

选好了语言,那么剩下的问题就是选择什么电脑来编程了,这在国内似乎就不是个问题,反正不都是Windows呗?2013年前我用的也是Windows,但是近几年在接国外的项目时我注意到,美国的程序员用的基本都是Mac,就连一些发展中国家的程序员也是用Mac居多。网站上的文档,全是以类Unix系统为范本的,所以我觉得越来越别扭,终于有一天我彻底放弃了Windows。现在我的结论是:最好是使用Mac,不行就用Linux(可以装在虚拟机里),再不行就在Windows里装两样东西:Bash和能提供Unix风格换行符的文本编辑器。

这是我现在的电脑:

Git的配置

Git在Linux/Mac中没什么要特别注意的地方,但在Windows中,安装时它会问你2个问题,要选择

使git命令仅能在Bash中运行,能起到最好的隔离作用。Windows里的Bash是啥?是一个模拟Linux的命令行环境,它会和Git一起被安装,在Bash里面你可以尽情使用大神们提到的Linux命令如lsgrep。千万不要选择让git在cmd中运行,这种“混合”会有各种各样的问题。

要知道,Git和Linux的发明者是同一个人,所以Git天生就更适合在Bash里运行。

至于选 Checkout as-is, commit as-is,也就是不自动修正换行符,许多人可能会反对。我说下我的理由:

浏览器

下面岔开些话题,说说浏览器控件。

众所周知在中国,网银都得装控件。但我不喜欢装各种控件。每多装一个控件,你的浏览器和系统就会多一份不稳定,所以浏览器越“纯”越好。再说,如果你是程序员,那你肯定知道怎样防木马,你中木马的概率极低,为什么还要被强制装上这些程序?

但若不装任何控件,网银就成了大问题,Flash也成了大问题。好在都有解决方法。

网银方面,你可以开一个浦发的账户。浦发银行是中资银行里面唯一支持非控件登录的,不过你要使用Chrome或Firefox来访问才行。如果你想在所有浏览器里面非控件登录,那只能选择外资银行。我选择的是渣打,因为它比较平民化,域名也很赞(sc.com)。不过要注意,当你网购时,外资银行的支持十分有限,好在这也有解决办法。例如你在京东购物,付款时虽然支持的银行里看不到“渣打银行”,但可以选择“平台付款”中的“银联”,然后在银联网站中进入渣打的网银。或者你也可以使用“快捷支付”,渣打也支持快捷支付。

Flash方面,装个Chrome就可以了,Chrome集成了Flash。

不过你也肯定有必须登录中资银行网银的时候,这时怎么办呢?用神器:虚拟机。Windows中可以装Windows,Mac中也可以装Windows。你就在这个虚拟机里面尽情地装各种控件,而它和你的host系统是隔离的。而且,这个Windows还不用付钱,因为你可以使用 Windows 8.1 Enterprise 评估版,这个版本是给你试用90天,90天以后每开机半小时左右会自动重启一次。反正我们用网银也用不到半小时,这是不是很爽呢?

编辑器

我用的是 Sublime Text。

Sublime Text 是GitHub推荐的两款编辑器之一(另一款是TextMate)。在配置中,我首先做的就是把所有的自动化都禁止掉,什么输入左括号就补个右括号,输入一个引号就补一个引号,统统都禁用。这样就保证了我输入什么,就能得到什么,这在我看来,是基本的原则。

很多浏览器的控制台,还会自动感知并提示对象名称以帮助输入,这很酷,但是要遵守一个原则:只能提示,即使输入的是非法的,也不能自动纠正。在这方面做得最好的是Firefox,而Chrome就不够好。大家可以比较一下,在控制台输入document.getElement,然后回车,看看分别发生了什么?

如果你是“故意”输错,那么你会发现在Chrome中要通过很复杂的方法才能达到目的。

大神们为什么都喜欢用命令行?因为它忠实地还原你的键盘输入,甚至从理论上来说,即使没有显示器,你照样可以输入命令!但它最大(或许是唯一)的缺点,是不能精细到像素级别,像是缩进层次的提示(就是左边那一排排很细的灰色竖线)就无法做到了。所以,图形界面也还是有必要的。

许多人都用WebStorm,但我在虚拟机里试用后,感觉这不是我的菜,理由如下:

附:就在写本文时,我惊讶地发现GitHub自己也推出编辑器了,叫做Atom!它能否取代 Sublime Text 呢?我要等安装了之后才知道。

四色定理

语法高亮的目的,是且仅是:

“帮助程序员快速辨别不容易一下子看清的地方。”

仅此而已,它没必要具有任何其他的功能。所以,我真的不在乎它的颜色有多丰富,再说颜色丰富也不一定就好看,对吧?于是,我试验。结果我发现4种颜色,已经能够使我达到辨认的目的。这4种颜色分别表示:

如果你用白色背景,那么可以分别设为蓝、绿、灰、黑,如果你不喜欢灰的注释,那么可以把注释设为绿色,把字符串设为红色,这样就RGB三原色都有了。

许多配色方案还有什么黄色、紫色、褐色,这些我一概不要。为什么?

注意:在浅色背景下,4种颜色都不应过浅,这样才协调。特别是绿色和黄色,一定要深才好看。

本文中的代码颜色还挺多的,我用的是highlight.js这个第三方库(不太好,经常有解析错误),以后我可能会自己写个HTML页面的语法高亮器,只使用4种颜色,但必须精准。

扁平化目录结构

当项目中充斥着:

    require("../../../file")

这显得很丑陋,也很不可靠。我的解决办法是:所有的源代码文件都放在一个目录里,并且目录只能有一层。用点号表示层次,代替目录的作用。

例如,你可以把abc/def/ghi.js扁平化,改成abc.def.ghi.js。如果你想要列出abc.def下的所有文件,ls abc.def.*即可。使用点号分隔,有再多的文件也不会使人感到混乱。

我还反对为某个类型专门建一个目录的做法。例如许多人喜欢建一个images目录,然后把所有的图片放进去。实际上,文件的后缀名已经起到了分类的作用,所以再建个目录等于增加了冗余。那为什么所有操作系统的用户目录里面都有Pictures、Videos、Music目录呢?我的理解是,这是为了方便普通用户,但对程序员而言并不是最好的。应该是,为某个特性、某个库或包(需要隔离)而建立目录。

合并顺序(慎用!)

这个方法可能不适用于大多数项目,所以请勿在你的项目上试验。

如果不用模块化,那么在“多文件、大项目”中有两个问题:名字冲突、合并顺序。名字冲突可以用闭包和命名空间解决,所以关键在于要解决合并顺序。

以我的mate仓库为例,其src目录:

    1.license.coffee
    2.prelude.coffee
    5.array.coffee
    5.compatibility.coffee
    5.main.coffee
    5.math.coffee
    6.timer.coffee
    9.coda.coffee

这样做还有几个额外的好处,是“完全模块化”所不具备的:

不过,这个方法也有个最大的问题,就是违反了当今世界的潮流:模块化。所以,到底适不适合别的项目,说不准。

注意:数字应具备相同的位数。通常来说,1位数已足够,例如:5为普通级别,1为最先,9为最后。如果要有超过9个级别,那么必须每个数字都是2位数,例如:01、02,切不可省略01中的0,这样电脑才会以正确的顺序列目录和合并文件。

代码文件的风格

我在编程时经常遇到的问题是:

我的解决方案也比较“猛”:

以Wishlist仓库中的一个文件为例:

结尾的换行符

我以前用 Visual Studio 写代码的时候,文件的结尾都不换行,想不通为什么许多人喜欢在文件的结尾输入一个空行,这不是多余的吗?直到我接触到Linux,才知道这是为什么:

用命令行工具cat合并文件,如果最后一行没有换行符,那么合并的结果将是灾难性的,例如:

文件a:

    This is A

文件b:

    This is B

没有换行符的话,会合并成为:

    This is AThis is B

文本文件必须以换行符结尾,已经成为一个约定俗成的法则,即使今天许多合并工具已经可以自动添加换行符,这个法则还是遵守为好。

代码质量

我衡量代码质量的标准是:

当只牵涉到一个标准时,很容易作出决断。使人头痛的通常是,某个标准的满足会以另一个标准的牺牲为代价。该如何权衡?下面分主题来探讨。

对齐?

许多“视觉控”们喜欢使用“对齐”,例如:


    var a = {
        abc      : "aaaa",
        defghijk : "bbbb",
        lmnop    : "cccc",
        qrstuvw  : "dddd"
    };

因为我也是视觉控,所以我一开始也喜欢这种方法,但是现在发现弊大于利,因为它是为了美观而增加冗余。如果你以后要给最长的那个项更名,那么就必须调整所有的。这是一种冗余,因为它把其他行的长度信息加入到了这行中。

所以,我现在反对这种“牵一发而动全身”的形式,除非:

我写了个函数:


    function preservedLength(lens, p) {
        var sum = 0;
        var maxLength = -1;
        lens.forEach(function(len) {
            maxLength = Math.max(maxLength, len);
            sum += len;
        });
        var average = sum / lens.length;
        var t = 0;
        lens.forEach(function(len) {
            t += Math.pow(len - average, 2);
        });
        var sd = Math.sqrt(t / (lens.length - 1));
        var estimate = average + sd / (Math.sqrt((1 - p) * 2));
        return Math.ceil(Math.max(maxLength, estimate) - maxLength);
    }

上例中,长度分别是3、8、5、7,那么我们用preservedLength([3, 8, 5, 7], 0.95)得出的结果是:需要再预留5个空格,以确保在添加或修改一个项时至少有95%的概率不会牵一发而动全身。


    var a = {
        abc           : "aaaa",
        defghijk      : "bbbb",
        lmnop         : "cccc",
        qrstuvw       : "dddd"
    };

但是,最好的办法,我认为,是根本不使用对齐。

for与forEach

传统中,我们若要对数组的每个元素进行操作,那得用for关键字。不过现在可以用数组的方法forEach,不但能在所有场合都替代for,而且还有额外的好处。


    for (var i = 0; i < list.length; i++) {
        (function() {
            var filename = list[i];
            fs.readFile(filename, function() {
                compile(filename);
            });
        })();
    }

作为JavaScript程序员的你,肯定写过上面的这类代码。当循环内部有异步回调函数的时候,我们只能写一个闭包,来hold住filename变量。不然,你将发现回调函数内每个filename都相同了!那如果我们用forEach,是否还需要多加一层呢?


    list.forEach(function(filename) {
        fs.readFile(filename, function() {
            compile(filename);
        });
    });

forEach的参数本身就是个函数,所以它天生就能hold住filename变量。它唯一的缺点,就是如果你在任何场合都用forEach的话,会有一点点性能上的损失,没有不含函数的for执行得快。不过说实话,JavaScript里没必要关注这方面的性能损失,就算在Java、C#里面这也是很难控制的,也只有C++才有方法(比如内联)来减少函数调用本身造成的性能损失。所以如果没有兼容 IE 6,7,8 的要求,那么最好是始终使用forEach。

--------

未完待续。

我正在做的项目

我目前专注于Wishlist和Mate项目,如果你觉得下面这些项目有创意,欢迎你一起来为它们作些贡献。

如果你发现我在这篇文章中介绍的方法有任何缺陷,请务必告诉我。

https://github.com/zhanzhenzhen
https://github.com/zhanzhenzhen/wishlist | 教程
https://github.com/zhanzhenzhen/mate | 教程
https://github.com/zhanzhenzhen/js-bundler
https://github.com/zhanzhenzhen/doc-html

zhanzhenzhen@hotmail.com