- A+
语句和表达式
花括号的对齐方式
-
风格一
左括号放置在块语句中第一句代码的末尾
if (flag) { doSomething(); } else { doSomethingElse(); }
解释:这里所说的块语句是包含条件(循环)控制语句的,比如这个例子中,块语句的第一句代码实际是if语句所在得到行
这种风格继承自Java
-
风格二
花括号的第二种对齐风格是将制作花括号放置在块语句首行的下一行
if (flag) { doSomething(); } else { doSomethingElse(); }
这种风格是随着C#流行起来的,因为Visual Studio强制使用这种对齐方式
块语句间隔
-
风格一
语句名、圆括号和左花括号之间没有空格间隔
if(flag){ doSomething(); }
看起来较为紧凑
-
风格二
括左圆括号之前和右圆括号之后各添加一个空格
if (flag) { doSomething(); }
-
风格三
左圆括号后和右圆括号前各添加一个空格
if ( flag ) { doSomething(); }
switch语句
缩进
-
风格一
switch(condition) { case "first": // 代码 break; case "second": // 代码 break; default: // 代码 }
- 每条case语句相对于switch关键字都缩进一个层级
- 从第二条case语句开始,每条case语句前后都各有一个空行
-
风格二
switch(condition) { case "first": // 代码 break; case "second": // 代码 break; default: // 代码 }
- 每条case语句相对于switch关键字都没有缩进
- 每条case语句前后没有空行
case语句的“连续执行”
“执行完一个case后连续执行下一个case”,这是否是一种广被认可的实践,但也是倍受争议的一个问题。不小心省略case末尾的break是很多bug的罪魁祸首,因此Douglas Crockford提出所有的case都应当以break、retur或throw做结尾,有很多人认为case的连续执行是一种可以接受的编程方法,只要程序逻辑非常清晰即可
switch(condition) { // 明显的依次执行 case "first": case "second": // 代码 break; case "third": // 代码 /* fail through */ default: // 代码 }
这段switch语句中,有两个明显的“连续执行”,程序执行完成第一个case后会继续执行第二个case,我们认为这种逻辑是合理的。因为第一个case语句中并没有需要执行的语句,也没有任何其他语句将这两个case语句分割开
第二个例子是case "third"执行后继续执行default里的逻辑。这个过程已经在注释中明确说明了,这是程序编写者故意为之。这段代码由于加了注释,我们可以清晰地看到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可以用局部变量和函数的形式来访问特定的对象的属性和方法,这样就可以将对象前缀统统省略掉。如果一段代码重书写了很多对象成员,则可以使用with语句来缩短这段代码。例如:
var book = { title: "可维护的JavaScript", author: "Nicholas C. Zakas" }; var message = "The book is "; with (book) { message += title; message += " by " + author; }
在这个例子中,with语句花括号内的代码中的book的属性都是通过局部变量来读取的,以增强标识符的解析。问题是我们很难分辨title和author出现在哪个位置,也很难分辨出message到底是局部变量还是book的一个属性。实际上这种困惑对开发者的影响更甚,JavaScript引擎和压缩工具无法对这段代码进行优化,因为它们无法猜出代码的正确含义。
在严格模式下,with是被明令禁止的,如果使用就会报语法错误
for循环
for循环有两种可以更改循环的执行过程(除了使用return和throw语句)。
break
第一种方式是使用break语句。不管所有的循环迭代又没有执行完毕,使用break总是可以立即退出循环。
var values = [1, 2, 3, 4, 5, 6, 7, 8], i, len; for (i = 0, len = values.length; i < len; i++) { if (i == 2) { break; // 迭代不会继续 } process(values[i]); }
这里的循环体只会执行两次,然后在第三次执行 process()之前就终止循环了,尽管values数组中的元素超过了三个。
continue
第二种更改循环执行过程的方法是使用continue。continue语句可以立即退出(本次)循环,而进入下一次循环迭代
var values = [1, 2, 3, 4, 5, 6, 7, 8], i, len; for (i = 0, len = values.length; i < len; i++) { if (i == 2) { continue; // 跳出本次迭代 } process(values[i]); }
这里循环体将执行两次,跳过第三次迭代,而后进入第四次迭代,循环继续执行直到最后一次迭代(如果中途不再会打断的话)
Crockford的编程规范不允许使用continue。他主张代码中与其使用continue不如使用条件语句。比如说上一个语句修改为
var values = [1, 2, 3, 4, 5, 6, 7, 8], i, len; for (i = 0, len = values.length; i < len; i++) { if (i != 2) { process(values[i]); } }
作者推荐尽可能避免使用continue,但也没有理由完全禁止使用,它的使用应当根据代码可读性来决定
for-in循环
for-in循环是用遍历对象属性的。不用定义任何控制条件,循环将会有条不紊地遍历每个对象属性,并返回属性名而不是值。
for-in循环有一个问题。就是他不仅变量对象的实例属性,同样还遍历从原型链上继承来的属性。当遍历自定义对象的属性时,往往会因为意外结果而终止。处于这个原因,最好使用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]); } }
作者推荐使用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, 8], i; for (i in values) { process(values[i]); }
使用for-in遍历数组会造成潜在错误,记住,for-in循环是用来对实例对象和原型链中的键(key)做遍历的,而不是用来遍历包含数字索引的数组的。因此for-in循环不应当用于这种场景