《编写可维护的JavaScript》读书笔记之编程风格-语句与表达式

语句与表达式

在 JavaScript 中,诸如 if 和 for 之类的语句有两种写法,使用花括号包裹的多行代码或者不使用花括号的单行代码。

【示例】:

// 不好的写法,尽管这是合法的JavaScript的代码
if(condition)
    doSomething();
// 不好的写法,尽管是合法的JavaScript代码
if(condition) doSomething();
// 不好的写法,尽管是合法的JavaScript代码
if (condition)	{ doSomething(); }
// 好的写法
if (condition) {
    doSomething();
}

【建议】:不论块语句包含多行代码还是单行代码,都应当总是使用花括号。

【问题】:省略花括号造成的疑惑,很难看出作者的意图。因为你不知道作者是想满足 condition 条件后,执行下面两条语句而忘记加花括号,还是只想执行第一条语句。

if (condition)
    doSomething();
    doSomethingElse();

【改进】:这两段代码都有明显的缩进错误,但通过花括号我们可以很快地看出作者的意图,并做出合适的修改。

if (condition) {
    doSomething();
}
    doSomethingElse();

if (condition) {
    doSomething();
doSomethingElse();
}

【建议】:所有的块语句都应当使用花括号。

  • if
  • for
  • while
  • do…while…
  • try…catch…finally

花括号的对齐方式

Java 风格

将左花括号放置在块语句中第一句代码的末尾。

【示例】:

if (condition) {
    doSomething();
} else {
    doSomethingElse();
}

【编程指南】:Crockford 编程规范、jQuery 核心风格指南、SproutCore 编程风格指南、Google 的 JavaScript 风格指南以及 Dojo 编程风格指南都出现过。

C# 风格

将左花括号放置于块语句首行的下一行。

【示例】:

if (condition)
{
    doSomething();
}
else
{
    doSomethingElse();
}

【说明】:当前并无主流的 JavaScript 编程规范推荐这种风格,Google JavaScript 明确禁止这种用法,以免导致错误的分号自动插入。

块语句间隔

【风格一】:在语句名、圆括号和左花括号之间没有空格间隔。

if(condition){
    doSomething();
}

【说明】:Dojo编程风格指南推荐使用这种风格。

【风格二】:在左圆括号之前和右圆括号之后添加一个空格。

if (condition) {
    doSomething();
}

【说明】:Crockford 编程规范和 Google JavaScript 风格指南推荐。

【风格三】:在左圆括号前后和右圆括号前后各添加一个空格。

if ( condition ) {
    doSomething();
}

【说明】:jQuery核心风格指南文档规定了这种风格,因为它使语句中的各个部分都非常清晰和易读。

【建议】:推荐第二种风格,是第一种和第三种风格的折中。

switch 语句

JavaScript switch 语句中可以使用任意类型值,任何表达式都可合法地用于 case 从句。但在其他语言中则必须使用原始值和常量。

缩进

【风格】:Java。

switch(condition) {
    case "first":
        // 代码
        break;
        
    case "second":
        // 代码
        break;
    
    case "third":
        // 代码
        break;
    
    default:
        // 代码
}

【特点】:

  • 每条 case 语句相对于 switch 关键字都缩进一个层级。
  • 从第二条 case 语句开始,每条 case 语句前后各有一个空行。

【风格】:Crockford 编程规范和 Dojo 编程风格指南。

switch(condition) {
case "first":
    // 代码
    break;
case "second":
    // 代码
    break;
case "third":
    // 代码
    break;
default:
    // 代码
}

【说明】:case 关键字保持和 switch 关键字左对齐,同时在语句中没有空行的存在。

case 语句的“连续执行”

“执行完一个 case 后连续执行(fall through)下一个 case”,这是否是一种广被认可的实践,也是备受争议的一个问题。不小心省略 case 末尾的 break 是很多 bug 的罪魁祸首。如果某个 case 执行结束后直接进入下一个 case,JSLint 会给出警告。

【说明】:很多人认为 case 的连续执行是一种可接受的编程方法,只要程序逻辑非常清晰即可。

switch(condition) {
    
    // 明显的依次执行
    case "first":
    case "second":
        // 代码
        break;
    
    case "third":
        // 代码
        
        /* fail through */
    default:
        // 代码
}
  • Crockford 编程规范禁止 switch 语句中出现连续执行(fall through)的。
  • jQuery 核心风格指南允许 case 的连续执行写法。
  • Dojo 编程风格指南给出了在连续执行中添加注释的例子。

【建议】:只要是有意为之并且添加了注释,就可以使用 case 语句的连续执行。

default

【讨论】:switch 语句中另外一个需要讨论的议题是:是否需要 default。

【观点一】:无论何时都不应当省略 default,哪怕 default 什么都不做。

switch(condition) {
    case "first":
        // 代码
        break;
        
    case "second":
        // 代码
        break;
    
    default:
        // default 中没有逻辑
}

【观点二】:在没有默认行为且写了注释的情况下省略 default。

switch(condition) {
    case "first":
        // 代码
        break;
    
    case "second":
        // 代码
        break;
    
    // 没有 default
}

with 语句

with 语句可以更改包含的上下文解析变量的方式。通过 with 可以用局部变量和函数的形式来访问特定对象的属性和方法,这样就可以将对象前缀统统省略掉。

【示例】:

var book = {
    title: "Maintainable JavaScript",
    author: "Nicholas C. Zakas"
};

var message = "The book is ";

with (book) {
    message += title;
    message += " by " + author;
}

【问题】:很难分辨出 title 和 author 出现在哪个位置,也难分辨出 message 到底是局部变量还是 book 的一个属性。实际上这种困惑对开发者的影响更甚,JavaScript 引擎和压缩工具无法对这段代码进行优化,因为它们无法猜出代码的正确含义。

【说明】:在严格模式下,with 语句是被明确禁止的,如果使用则报语法错误。Crockford 编程规范和 Google 的 JavaScript 风格指南禁止使用 with。

【推荐】:避免使用 with 语句,因为你无法将你的代码运行于严格模式之中。

for 循环

for 循环有两种:一种是传统的 for 循环,是 JavaScript 从 C 和 Java 中继承而来;另外一种是 for-in 循环,用来遍历对象的属性。

传统 for 循环

往往用于遍历数组成员。

有两种方法可以更改循环的执行过程(除了使用 return 或 throw 语句)。

  • break:不管所有的循环迭代有没有执行完毕,使用 break 总是可以立即退出循环。
  • continue:可以立即退出本次循环,而进入下一次循环迭代。

【说明】:

  • Crockford 编程规范不允许使用 continue。他主张代码中与其使用 continue 不如使用条件语句。解释说这种方法对于开发者来说更容易理解而且不容易出错。
  • Dojo 编程风格指南明确指出可以使用 continue 和 break。

【推荐】:尽可能避免使用 continue,但也没有理由完全禁止使用,它的使用应当根据代码可读性来决定。

【注意】:当使用了 continue 时,JSLint 会给出警告。而 JSHint 不会给出警告。

for-in 循环

for-in 循环是用来遍历对象属性的。不用定义任何控制条件,循环将会有条不紊地遍历每个对象属性,并返回属性名而不是值。

【问题】:for-in 循环不仅遍历对象的实例属性(instance property),同样还遍历从原型继承来的属性。当遍历自定义对象的属性时,往往会因为意外的结果而终止。

【解决】:出于上述问题,最好使用 hasOwnProperty() 方法来为 for-in 循环过滤出实例属性。

var prop;

for (prop in object) {
    if (object.hasOwnProperty(prop)) {
        console.log("Property name is " + prop);
        console.log("Property value is " + object[prop]);
    }
}

【说明】:Crockford 编程规范要求所有的 for-in 循环都必须使用 hasOwnProperty()。默认情况下,对于循环体中没有使用 hasOwnProperty() 的 for-in 循环,JSLint 和 JSHint 都会给出警告。

【推荐】:总是在 for-in 循环中使用 hasOwnProperty(),除非你想查找原型链,这时就应当补充注释。

var prop;

for (prop in object) { // 包含对原型链的遍历
    console.log("Property name is " + prop);
    console.log("Property value is " + object[prop]);
}

【注意】:for-in 循环是用来遍历对象的,而不应该遍历数组成员。

// 不好的用法
var values = [1, 2, 3, 4, 5, 6, 7],
    i;

for (i in values) {
    process(values[i]);
}

【说明】:该用法在 Crockford 编程规范、Google 的 JavaScript 风格指南中是禁止的,因为这会造成潜在的错误。记住,for-in 循环是用来对实例对象和原型链中的键(key)做遍历的,而不是用来遍历包含数字索引的数组的。因此 for-in 循环不应当用于这种场景。

你可能感兴趣的:(JavaScript)