[译] Javascript代码约定

本文译自Javascript大师Douglas Crockford的文章(2016.10.27版),以飨英文不太好的同学。如果英文过关,建议读原文

原文 Code Conventions for the JavaScript Programming Language

排版规则

粗体:Javascript关键词、操作符等
斜体:译者补充说明
部分代码块为译者所加,前面注有:// 译者加

译文全文

本文是Javascript编程过程中用到的一系列约定和规则。
对于一个组织来说,从长远来看,软件的价值与其代码的质量成正比例。在一个程序的整个生命中,会经历很多双眼、很多双手。如果一个程序能清晰的传达其结构和特性,那么未来修改时,这些结构和特性被破坏的可能性会较小。代码约定就是为了降低程序的脆弱性(brittleness)。
JavaScript代码都是要公开发布的,所以它总是需要具有发布质量。简洁是有价值的。

JavaScript文件

应该保存为 .js 文件。
JavaScript代码不应该嵌入到HTML文件中,除非此代码仅为这一个HTML文件所用(译的欠妥)。将JavaScript嵌入到HTML会增大文件,导致无法通过缓存压缩等机制减小文件。

空白(Whitespace)

下面这些规则一直以来都是文字书写的好习惯。除非有明显的好处,否则不应该被打破。

空行(blank line),将逻辑上有关联的代码放在一块,可以提高可读性。
空格(blank space),在以下情况中都应该使用:

  • 一个关键词后跟一个 ( 时,应该用一个空格隔开。应用空格,可以使不是调用的地方看起来不像调用(Spaces are used to make things that are not invocations look less like invocations.)。如下例,ifwhile 之后应该有空格:
while (true) {
  • 在function和调用( ( )之间不应该有空格。这样可以将关键词和函数调用区分开来。
// 译者加
some_func(a, b);
  • 关键词 function 后面总是要跟空格
// 译者加
function foo() { ...
var bar = function () { ...
  • 一元操作符和其操作数之间不应该有空格,除非操作符是一个单词,比如 typeof
// 译者加
some_var++;
++some_var;
  • 二元操作符和其操作数之间总是用一个空格隔开,这几个例外:. ( [
// 译者加
var c = a + b;
  • 逗号 , 之后总是跟空格或换行
  • 用于语句结束的分号 ; 后总是需要换行
  • for 语句中的分号 ; 后总是跟一个空格
// 译者加
for (var i = 0; i < 5; i++) {

每一个语句都应该跟当前缩进对齐,最外层缩进对齐编辑区左侧。当上一行的最后一个标识为 { [ ( 时,本行缩进4个空格;相应的关闭标识 } ] ) 应新起一行,缩进减4个空格

// 译者加
function foo() {
      while (true) {
          console.log('hello');
      }    
}

三元操作符看起来比较晕,所以,? 总是新起一行并缩进4个空格,: 也总是新起一行,与 ? 对齐。条件判断需用 ( ) 包起来:

var integer = function (
      value,
      default_value
) {
      value = resolve(value);
      return (typeof value === "number")
          ? Math.floor(value)
          : (typeof value === "string")
               ? value.charCodeAt(0)
               : default_value;
};

如果 . 是一行的第一个字符,缩进4个空格。

应避免一行过于长。如果一个语句在一行放不下,那就有必要将它分开。最好在 { [ ( ,换行,或者 . ? :换行。如果在这地方换行不合适,那就在操作符之后换行,新行缩进8个空格。但是这8个空格不改变当前缩进。

块(case catch default else finally)不是语句,故不需要缩进。

Tab(制表符)和空格不应该混用。二者只选其一,以避免两者都用带来的问题。个人偏好设置并不可靠。Tab空格并不比对方更具优势。50年前,Tab的优势在于节省内存,但摩尔定律已经否定了这一优势(此处不是很懂)空格Tab有一个明确的优势:目前还没有一个可依赖的标准规定一个Tab占几个空格,但毫无争议的是,一个空格确切的占一个空格。所以,总是使用空格。如果必须,你可以在编辑时使用Tab,但确保在提交(commit)时转为空格。或许有一天我们将最终有一个关于Tab的统一标准,不过在那天到来之前,使用空格,是明智的选择。

注释(Comment)

应该不吝啬注释。留下一些信息,有助于未来的人(包括你自己)理解你做了什么、为什么要做。注释应该好好写、写清楚,要像写代码一样写注释。一些小幽默也是鼓励的,不过困难和抱怨就不要写了。

有一点很重要:注释应该保持“新鲜”(up-to-date)。错误的注释反而会使程序更难读、更难懂。

注释应该有意义,着重说明不能从代码里一眼就看出来的隐含意义。但也不要浪费时间写下面这样的注释:

i = 0; // 把i设为0

变量声明(Variable Declarations)

所有的变量都应该在使用之前声明。JavaScript并不强制如此,但这样做可以使程序更易读,并且容易发现未声明的变量可能是隐式的(这句译的欠妥)。隐式的全局变量应该杜绝。尽量少用全局变量。

推荐:变量的声明语句后跟注释。如果可能,以字母顺序排列:

var currentEntry; // currently selected table entry
var level;        // indentation level
var size;         // size of table

JavaScript中的var没有作用域,故,将变量声明在块(block)中会给有C语言经历的同学造成迷惑。

函数声明(Function Declaration)

所有的function都应该先声明后使用。内部函数(inner function)应跟在var后面(即先声明变量,再声明内部函数),这样可以清楚知道当前作用域(scope)有哪些变量。

函数名和 ( 之间不应该有空格;){ 之间应该有一个空格。函数体缩进4个空格,} 应和函数声明的那一行开始对齐。

function outer(c, d) {
    var e = c * d;
    var x;  // no use

    function inner(a, b) {
        return (e * a) + b;
    }

    return inner(0, 1);
}

这样的约定很适合JavaScript,因为在Javascript中函数和object定义(object literal)可以在任何可以放表达式的地方。可以最大程度的提高内部函数和复杂结构的可读性。

function getElementsByClassName(className) {
    var results = [];
    walkTheDOM(document.body, function (node) {
        var array;                // array of class names
        var ncn = node.className; // the node's classname

// If the node has a class name, then split it into a list of simple names.
// If any of them match the requested name, then append the node to the list of results.

        if (ncn && ncn.split(" ").indexOf(className) >= 0) {
            results.push(node);
        }
    });
    return results;
}

如果是匿名函数,在关键词 function( 之间应该有一个空格。如果没有,看起来好像这个函数的名字是function,这当然是错误的解读。

div.onclick = function (e) {
    return false;
};

that = {
    method: function () {
        return this.datum;
    },
    datum: 0
};

应尽量少用全局函数

如果一个函数被即刻调用(to be invoked immediately),那么整个调用表达式应用 ( ) 包起来,以清楚地表达:产生的值是函数执行的结果,而非函数本身

var collection = (function () {
    var keys = [];
    var values = [];

    return {
        get: function (key) {
            var at = keys.indexOf(key);
            if (at >= 0) {
                return values[at];
            }
        },
        set: function (key, value) {
            var at = keys.indexOf(key);
            if (at < 0) {
                at = keys.length;
            }
            keys[at] = key;
            values[at] = value;
        },
        remove: function (key) {
            var at = keys.indexOf(key);
            if (at >= 0) {
                keys.splice(at, 1);
                values.splice(at, 1);
            }
        }
    };
}());

命名(Names)

名字应该由26个字母的大小写(A...Z, a...z),10个数字(0...9),以及下划线:_ 组合而成。避免使用国际字符,因为他们可能被误读。不要使用 $ 和 \

不要将 _ 用于名字首字母。这样做有时是为了表示“私有”(privacy),但并不能真正的提供私有(保护)。如果私有很重要,应使用闭包(closure)。避免使用这种名不副实的做法。

绝大多数变量名和function名应该以小写字母开头。

必须用 new 调用的构造函数(constructor function),名字应该以大写字母开头。如果 new 缺失,JavaScript并不视为一个编译时(compile-time)错误,也不视为一个运行时(run-time)错误,但却可能有非预期的结果。首字母大写是我们唯一的保护机制。

全局变量应该全部用大写字母组成。

语句(Statements)

简单语句(Simple Statements)

一行应最多包含一个语句。每一个简单语句都应以 ; 结束。注意:函数定义(function literal)或对象定义(object literal)的赋值语句(assignment statement)也是赋值语句,故也应以 ; 结束。

// 译者加
var foo = {
    name: 'x'
};

var bar = function () {
    return 'bar';
};

JavaScript允许任何表达式(expression)作为语句(statement)使用。这会掩盖一些错误,有 ; 时更甚。可以作为语句的表达式仅建议:赋值调用delete

复合语句(Compound Statements)

复合语句:由 { } 包起来的一系列语句

  • 包起来的语句应缩进4个空格
  • { 应该在复合语句开始的那一行行尾
  • } 应该新起一行,并与相应的 { 所在行行首对齐
  • { } 应包起所有的语句,即使只有一条语句(当它作为控制结构(control structure)的一部分时,比如 if for),这样可以避免后续添加语句时引入错误。
// 译者加
if (true) {
      console.log('true');
      // do_some_th
} else {
      console.log('false');
}
标签(Labels)

应避免使用语句标签。只有下面语句才可应用:while do for switch

return语句

返回的值表达式(value expression)必须与 return 关键词在同一行,以避免插入 ; (后半句不很懂)

if语句

if 类的语句应该有如下形式:

    if (condition) {
        statements
    }
    
    if (condition) {
        statements
    } else {
        statements
    }
    
    if (condition) {
        statements
    } else if (condition) {
        statements
    } else {
        statements
    }
for语句

for 类的语句应该有如下形式:

    for (initialization; condition; update) {
        statements
    }
while语句

while 语句应该有如下形式:

    while (condition) {
        statements
    }
do语句

do 语句应该有如下形式:

    do {
        statements
    } while (condition);

跟其他复合语句不同的是:do 语句总是以 ; 结束

switch语句

switch 语句应该有如下形式:

    switch (expression) {
    case expression:
        statements
    default:
        statements
    }

caseswitch 对齐,以避免缩进太多。因 case 不是语句,故不需要“像”语句。

每一组statements(default除外)都应该以 break / return / throw 结束。不应穿透(fall through)

continue语句

避免使用该语句。因它会使控制流(control flow)不清晰。

with语句

不应该使用

{ } 和 [ ]

使用 { } ,而不用 new Object()。
使用 [ ] ,而不用 new Array()。
当成员的名字是整数序列时,使用数组(array)。
当成员的名字是无序字符串或名字时,使用对象(object)。

逗号操作符(, Operator)

不应使用逗号操作符。(并不是指我们广泛使用的逗号分隔符

赋值表达式(Assignment Expressions)

避免在 if while 语句的条件判断中进行赋值操作 ,下面这句:

if (a = b) {

是正确的吗?还是下面这句:

if (a == b) {

才是本意?所以,应避免这种容易引起歧义的写法

=== and !== 操作符

请使用 ===!== 操作符。==!= 有强制转换,应避免使用。

引起混淆的加号、减号

不要在 + 后面跟另一个 +++,因这种做法会引起混淆。而是应该加上 **( ) ** ,以清晰表达你的意图。

total = subtotal + +myInput.value;

最好改写为:

total = subtotal + (+myInput.value);

如此,+ + 就不会误读为 ++

eval是恶魔(eval is Evil)

eval 函数是JavaScript中最被误用的,不要用它。
eval 有别名。(不很懂)
不要使用 Function 构造函数。(不很懂)
不要给 setTimeout setInterval 传字符串。

用心编程,你也能长满大胡子 ;)


800px-Douglas_Crockford.jpg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容