Node開發規範v1.0

李济宏(Amadeus)發表於2024-11-27

一、空格與格式

(一)縮排

採用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文件。

相關文章