[译] 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 传字符串。

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


[译] Javascript代码约定_第1张图片
800px-Douglas_Crockford.jpg

你可能感兴趣的:([译] Javascript代码约定)