一、空格與格式
(一)縮排
採用2個空格縮排,而不是tab縮排。 空格在編輯器中與字元是等寬的,而tab可能因編輯器的設定不同。2個空格會讓程式碼看起來更緊湊、明快。
變數宣告
永遠用var宣告變數,不加var時會將其變成全域性變數,這樣可能會意外汙染上下文,或是被意外汙染。 在ECMAScript 5的strict模式下,未宣告的變數將會直接丟擲ReferenceError異常。
需要說明的是,每行宣告都應該帶上var,而不是隻有一個var,示例程式碼如下:
var assert = require('assert')
, fork = require('child_process').fork
, net = require('net')
, EventEmitter = require('events').EventEmitter;
錯誤示例如下所示:
var assert = require('assert')
, fork = require('child_process').fork
, net = require('net')
, EventEmitter = require('events').EventEmitter;
(二)空格
在運算子前後需要加空格,比如+、-、*、%、=等運算子前後都應該存在一個空格,示例如下:
var foo = 'bar' + baz;
錯誤的示例如下所示:
var foo='bar'+baz;
此外,在小括號前後應該存在空格,如:
if (true) {
// some code
}
錯誤的示例如下所示:
if(true){
// some code
}
(三)單雙引號
由於雙引號在別的場景下使用較多,在Node中使用字串時儘量使用單引號,這樣無需轉義,如:
var html = 'CNode';
而在JSON中,嚴格的規範是要求字串用雙引號,內容中出現雙引號時,需要轉義。
大括號的位置
一般情況下,大括號無需另起一行,如
if (true) {
// some code
}
錯誤的示例如下:
if (true)
{
// some code
}
(四)逗號
逗號用於變數宣告的分隔或是元素的分隔。如果逗號不在行結尾,前面需要一個空格。此外,逗號不允許出現在行首,比如: var foo = 'hello', bar = 'world'; // 或是 var hello = { foo: 'hello', bar: 'world' }; // 或是 var world = ['hello', 'world'];錯誤示例如下:
var foo = 'hello'
, bar = 'world';
// 或是
var hello = {foo: 'hello'
, bar: 'world'
};
// 或是
var world = [
'hello'
, 'world'
];
(五)分號
給表示式結尾新增分號。儘管JavaScript編譯器會自動給行尾新增分號,但還是會帶來一些誤解,示例如下:
function add() {
var a = 1, b = 2
return
a + b
}
將會得到undefined的返回值。因為自動加入分號後會變成如下的樣子:
function add() {
var a = 1, b = 2;
return;
a + b;
}
後續的a + b將不會執行。
而如下的程式碼:
x = y
(function () {
}())
執行時會得到:
x = y(function () {}())
由於自動新增分號可能帶來未預期的結果,所以新增上分號有助於避免誤會。
二、命名規範
在編碼過程中,命名是重頭戲。好的命名可以令程式碼賞心悅目,帶來愉悅的閱讀享受,令程式碼具有良好的可維護性。命令的主要範疇有變數、常量、方法、類、檔案、包等。
(一)變數命名
變數名都採用小駝峰式命名,即除了第一個單詞的首字母不大寫外,每個單詞的首字母都大寫,詞與詞之間沒有任何符號,如:
var adminUser = {};
錯誤的示例如下:
var admin_user = {};
(二)方法命名
方法命名與變數命名一樣,採用小駝峰式命名。與變數不同的是,方法名儘量採用動詞或判斷性詞彙,如:
var getUser = function () {};var isAdmin = function () {};
User.prototype.getInfo = function () {};
錯誤示例如下:
var get_user = function () {};var is_admin = function () {};
User.prototype.get_info = function () {};
(三)類命名
類名採用大駝峰式命名,即所有單詞的首字母都大寫,如:
function User {
}
(四)常量命名
作為常量時,單詞的所有字母都大寫,並用下劃線分割,如:
var PINK_COLOR = "pink";
(五)檔案命名
命名檔案時,請儘量採用下劃線分割單詞,比如child_process.js和string_decode.js。如果你不想將檔案暴露給其他使用者,可以約定以下劃線開頭,如_linklist.js。
(六)包名
也許你有貢獻模組並將其打包釋出到NPM上。在包名中,儘量不要包含js或node的字樣,它是重複的。包名應當適當短且有意義的,如:
var express = require('express');
三、比較操作
在比較操作中,如果是無容忍的場景,請儘量使用=代替,否則你會遇到下面這樣不符合邏輯的結果:
'0' == 0; // true'' == 0 // true'0' === '' // false
此外,當判斷容忍假值時,可以無需使用=或。在下面的程式碼中,當foo是0、undefined、null、false、''時,都會進入分支:
if (!foo) {
// some code
}
四、字面量
請儘量使用{}、[]代替new Object()、new Array(),不要使用string、bool、number物件型別,即不要呼叫new String、new Boolean和new Number。
五、作用域
在JavaScript中,需要注意一個關鍵字和一個方法,它們是with和eval(),容易引起作用域混亂。
慎用with
示例程式碼如下:
with (obj) {
foo = bar;
}
它的結果有可能是如下四種之一:obj.foo = obj.bar;、obj.foo = bar;、foo = bar;、foo = obj.bar;,這些結果取決於它的作用域。如果作用域鏈上沒有導致衝突的變數存在,使用它則是安全的。但在多人合作的專案中,這並不容易保證,所以要慎用with。
慎用eval()
慎用eval()的原因與with相同。如果不影響作用域上已存在的變數,用它是安全的。另外,利用eval()的這個特性,也可以玩出一些好玩的特性來,比如wind.js利用它實現了流程控制,詳見第4章。在大多數情況下,基本上輪不到eval()來完成特殊使命。示例程式碼如下:
var obj = {foo: 'hello',bar: 'world'
};var key = (Math.round(Math.random() * 100) % 2 === 0) ? 'foo' : 'bar';var value = eval('(obj.' + key + ')');
上述程式碼多出現在新手中,實際只要如下一行程式碼即可完成:
var value = obj[key];
六、陣列與物件
在JavaScript中,陣列其實也是物件,但是兩者在使用時有些細節需要注意。
字面量格式
建立物件或者陣列時,注意在結尾用逗號分隔。如果分行,一行只能一個元素,示例程式碼如下:
var foo = ['hello', 'world'];var bar = {
hello: 'world',
pretty: 'code'
};
錯誤示例如下所示:
var foo = ['hello','world'];var bar = {
hello: 'world', pretty: 'code'
};
for in迴圈
使用for in迴圈時,請對物件使用,不要對陣列使用,示例程式碼如下:
var foo = [];
foo[100] = 100;for (var i in foo) {
console.log(i);
}
for (var i = 0; i < foo.length; i++) {
console.log(i);
}
在上述程式碼中,第一個迴圈只列印一次,而第二個迴圈則列印0~100,這並不滿足預期值。
不要把陣列當做物件使用
儘管在JavaScript內部實現中可以把陣列當做物件來使用,如下所示:
var foo = [1, 2, 3];
foo['hello'] = 'world';
這在for in迭代時,會得到所有值:
for (var i in foo) {
console.log(foo[i]);
}
也許你只是想得到hello而已。
七、非同步
在Node中,非同步使用非常廣泛並且在實踐過程中形成了一些約定,這是以往不曾在意的點。
非同步回撥函式的第一個引數應該是錯誤指示,並不是所有回撥函式都需要將第一個引數設計為錯誤物件。但是一旦涉及非同步,將會導致try catch無法捕獲到非同步回撥期的異常。將第一個引數設計為錯誤物件,告知呼叫方是一個不錯的約定。示例程式碼如下:
function (err, data) {
};
這個約定被很多流程控制庫所採用。遵循這個約定,可以享受社群流程控制庫帶來的業務編寫便利。
執行傳入的回撥函式
在非同步方法中一旦有回撥函式傳入,就一定要執行它,且不能多次執行。如果不執行,可能造成呼叫一直等待不結束,多次執行也可能會造成未期望的結果。
八、類與模組
在Node中,如果要將一個類作為一個模組,就需要在意它的匯出方式。
類繼承
一般情況下,我們採用Node推薦的類繼承方式,示例程式碼如下:
function Socket(options) {
// …
stream.Stream.call(this);
// …
}
util.inherits(Socket, stream.Stream);
匯出
所有供外部呼叫的方法或變數均需掛載在exports變數上。當需要將檔案當做一個類匯出時,需要透過如下的方式掛載:
module.exprots = Class;
而不是透過
exports = Class;
私有方法無需因為測試等原因匯出給外部,所以無須掛載。
九、註解規範
一般情況下,我們會對每個方法編寫註釋,這裡採用dox的推薦註釋,示例如下:
/**
- Queries some records
- Examples:
- ```
- query('SELECT * FROM table', function (err, data) {
- // some code
- });
- ```
- @param {String} sql Queries
- @param {Function} callback Callback
*/
exports.query = function (sql, callback) {
// …
};
dox的註釋規範源自於JSDoc。可以透過註釋生成對應的API文件。