這第一篇就談談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+規范,網上有幾種實現方式,可以參閱。