這第一篇就來談談NodeJs的一些程式細節吧。
1、遍歷數組
for (var i=0, l=arr.length; i<l; i++)
如此寫的好處是讓每次循環少一步取得陣列物件長度的操作,陣列長度越長,價值越明顯。
2、判斷變數的真假
if (a) {...} //a='', a='0', a=[], a={}
if條件判斷的結果分別為:false, true, true, true。這個結果和PHP的結果是不同的,不要混淆。還需要區分它和非恆等判斷相似的情況。
3、0值非恆等判斷
1 if (0 == '0') {...} //true 2 if (0 == []) {...} //true 3 if (0 == [0]) {...} //true 4 if (0 == {}) {...} //false 5 if (0 == null) {...} //false 6 if (0 == undefined) {...} //false
其實還有很多這種詭異的判斷,我只列出了較為常見的。如果想弄清楚其中的規則,請參考我的另一篇部落格文章:【JavaScript】深入分析JavaScript的關係運算和if語句。
4、parseInt的陷阱
var n = parseInt(s); //s='010'
此語句執行後n值為8,而非10。雖然很多人知道這一點,但是程式設計中難免會出錯,我深有體會。所以,最好用下面的方式來寫,就不會出錯了。
var n = parseInt(s, 10);
5、變數在使用前一定要先宣告
雖然,直接使用變數而不宣告也不會出錯,但是,這樣寫是很容易出錯的。因為解釋器會把它解釋成全域變量,很容易和其他全域變數重名而導致出錯。所以,一定要養成變數使用前要先聲明的好習慣。
6.循環中存在非同步的情況
for (var i=0, l=arr.length; i<l; i++) { var sql = "select * from nx_user"; db.query(sql, function(){ sys.log(i + ': ' + sql); }); //db.query为表查询操作,是异步操作 }
你會發現,輸出的結果都是相同的,而且是當i=arr.length-1時的輸出內容。因為JavaScript是單執行緒的,它會先執行完整個迴圈的同步內容之後,才會去執行其中的非同步操作。程式碼中的匿名回呼函數就是一個非同步回呼。執行到該函數的時候,for迴圈以及後面的一些同步操作都已經執行完畢。出於閉包原則,函數會保留for迴圈的最後一次迴圈的sql變數和i變數的內容,所以才會導致錯誤的結果。
那該怎麼辦呢?解決方法有兩種,一種是使用立即函數,如下:
for (var i=0, l=arr.length; i<l; i++) { var sql = "select * from nx_user"; (function(sql, i){ db.query(sql, function(){ sys.log(i + ': ' + sql); }); //db.query为表查询操作,是异步操作 })(sql, i); }
還有一個方法是將非同步操作部分提取出來,單寫一個函數,如下:
var outputSQL = function(sql, i){ db.query(sql, function(){ sys.log(i + ': ' + sql); }); //db.query为表查询操作,是异步操作 } for (var i=0, l=arr.length; i<l; i++) { var sql = "select * from nx_user"; outputSQL(sql, i); }
7 、在對大量資料作處理時,盡量避免循環巢狀。
因為循環巢狀的處理時間會隨著資料量的增加而呈指數級增長,所以應盡量避免。遇到這種情況,如果沒有更好的辦法,一般採取的策略是以空間換時間,也就是建立一張二級循環資料的Hash映射表。當然,還要具體情況具體分析。還有一點要說的是,某些方法本身就是一個循環體,如Array.sort()(該方法應該是用了兩層循環實作),在使用的時候需加註意。
8、盡量避免遞迴呼叫。
遞歸呼叫的優點是程式碼簡潔,實作簡單,而它的缺點很重要,說明如下:
(1)函數棧的大小會隨著遞歸層次成線性增長,而函數棧是有上限值的,當遞歸達到一定層數後函數棧就會溢出,從而導致程式出錯;
(2)每遞歸一層都會增加額外的壓棧和出棧操作,即函數呼叫過程中的保存現場和恢復現場。
所以,應盡量避免遞歸呼叫。
9.關於模組檔案的作用域隔離。
Node在編譯JavaScript模組檔案的時候,已經對其內容進行了頭尾包裝,如下:
(function(exports, require, module, __filename, __dirname){ 你的JavaScript文件代码 });
從而使每個模組檔案之間進行了作用域隔離。所以,當你寫NodeJs模組檔案的時候,不需要自己再加一層作用域隔離封裝了。如下面的程式碼格式,只會額外增加一層函數調用,是不建議的:
(function(){ ... ... })();
10、陣列和物件不要混用
以下是錯誤代碼的範例:
var o = []; o['name'] = 'LiMing';
陣列和物件混用可能會導致不可預測的錯誤。我的一個同事就遇到一個很奇怪的問題,先看程式碼:
var o = []; o['name'] = 'LiMing'; var s = JSON.stringify(o);
他以為物件o的name屬性會在JSON串中,結果就是沒有。當時我也很奇怪,但我有預感到是數組和物件混用的問題,試了一下,果然是它的問題。後來我在ECMA規範中查到,數組在序列化時是依照JA規則進行的。所以,要養成一個好的程式設計習慣,正確使用陣列和對象,不要混用。
11、promise優雅程式
相信接觸過nodeJs的人都有這樣的體驗,當異步回呼裡嵌套非同步回呼的時候,程式碼就顯得很混亂,缺乏易讀性。 nodeJs的這一窘境可以藉助promise來克服。 promise就像一個雕琢器,讓你的程式碼變得優雅、美觀。 promise有個A 規範,網路上有幾種實作方式,可以參考。