不知不覺的已經用Node.js有將近一年了,這裏我有幾條出自實踐的node.js建議送給剛剛入門的node.js的朋友。javascript
在JavaScript中,咱們能夠建立匿名對象和匿名函數。通常來講,匿名函數可讓代碼更加簡短精悍。html
然而,對這些對象或函數進行命名,則有利於調試和優化。如下是我從Chrome DevTool的文章中借用的圖片:java
很明顯,命名的實體更利於調試和優化。node
JavaScript的GC以一種相似引用計數的算法工做,一個對象當且僅當全部指向它的引用所有釋放以後,它自己纔會被釋放掉。git
然而可能你有其餘的閉包或者全局對象持有它的引用,從而阻止了垃圾回收。爲避免這一現象,應當儘早地解除沒必要要的引用:github
var some_var = new net.Server(); // other code... var i_want_it_temperoray = some_var; some_operation(i_want_it_temperoray); i_want_it_temperoray = null; // derefernce // other code...
若是閉包中尚有一個變量未能被釋放,那麼整個閉包都有可能沒法被回收,從而形成其它對象也沒法釋放。 web
如今有不少工具能夠輔助咱們監視內存的變化狀況,好比heapdump、webkit-devtools-agent等等。同時,這也更加說明了爲何要儘量使用命名而不是匿名的對象。算法
我用Node.js開發的項目代碼變化很是快,重構也很頻繁,經常會處處複製代碼。然而每當我這樣作的時候,後來都會發現某些變量要麼就是忘了更名,要麼就是有些變量壓根就不存在了,致使代碼變得極爲不穩定。chrome
JS是一門高度動態的語言,它不像C++這樣能夠利用編譯器作靜態檢查,所以你幾乎沒有機會依靠工具檢查出這些問題。因此個人建議是,儘量地本身再輸入一遍代碼,這樣IDE或者編輯器有機會利用代碼自動補全來爲你檢查。若是你的IDE還沒這功能,那你真得考慮換掉它了。數據庫
Node.js社區很是活躍,有成千上萬的現成模塊能夠取用,然而其中有些其實已經沒人管了。Node.js的API也經常發生變化,適配node v0.8.x的模塊,有可能不支持v0.10.x。
所以當你考慮引入新的模塊的時候,務必先去它的pull request列表或者issue列表看看,確認一下這個模塊是否是已經被拋棄,或者存在重大的隱患。
Node.js基於回調。由於回調的本質,咱們很容易寫出嵌套多層的回調函數。回調對於異步來講是好事,但對於代碼維護來講倒是壞事。
若是你發現代碼須要3層以上的回調函數嵌套,那你應該考慮一下,要不要使用async.js或者基於promise概念的模塊。
如下是一個用async.js寫出來的一系列異步操做:
async.auto([ 'init_logger': function(done){ set_handlers_to_logger(done); }, 'load_config': ['init_logger', function(done){ load_my_config(done); }], 'init_database': ['load_config', function(done){ connect_to_db_here(done); }], // 假定open_cache與數據庫無關 'open_cache': ['load_config', function(done){ open_cache_here(done); }], // warm_up一般用於從數據庫預先抓取一些數據 // 注意,warm_up只會在'init_database'以及'open_cache'結束後執行 'warm_up': ['init_database', 'open_cache', function(done){ fetch_some_data(done); }], 'init_routers': ['load_config', function(done){ install_routers(done); }], 'emit_out': ['warm_up', 'init_routers', function(done){ notify_others(done); }] ], function(err) { if(err){ // 在這處理異常 } });
我對promise相關的模塊不是很熟,但使用Q,應該能夠寫出這樣的代碼,,一樣易於閱讀:
Q.nfcall(function init_logger(){ set_handlers_to_logger(); }) .then(function load_config(){ load_my_config(); }) .then(function init_database(){ connect_to_db_here(); }) .then(function open_cache(){ open_cache_here(); }) .then(function warm_up(){ fetch_some_data(); }) .then(function init_routers(){ install_routers(); }) .then(function emit_out(){ notify_others(); }) .catch(function (error) { // 在這處理異常 }) .done();
選取什麼樣的庫來簡化邏輯通常都是隨便你。一般來講,async.js很是簡單,而Q則更加靈活強大。
好比說async.js中各個函數獨立而不嵌套,所以若是你想經過捕獲某個函數中的變量就顯得有些困難,而在Q中就可使用嵌套的
then
語句。
原本我還想寫一寫不使用任何輔助模塊的上述代碼,但其實我都很懷疑本身能不能寫對。
另外,你是CoffeeScript的擁泵麼?若是是,那你還能夠嘗試一下IcedCoffeeScript。IcedCoffeeScript在語言層面上提供了兩個很是有用的關鍵字: await
和defer
。基本上全部的流程控制均可以經過這兩個關鍵字進行改造。不過我本人對IcedCoffeeScript並不熟悉,因此就不在這獻醜了。這裏感謝brettof86向我介紹了這一利器,不過我還須要花些時間來熟悉它。
用Node.js的時候咱們可能會變得有點過於理想化了,所以很容易寫出下面這樣的聊天室代碼:
var net = require('net'); var clientList = []; var server = net.createServer(function(c) { //'connection' listener console.log('server connected'); clientList.push(c); c.on('end', function() { console.log('server disconnected'); unpipe_all(c, clientList); remove_from(c, clientList); }); clientList.forEach(function(item){ item.pipe(c); // 注意這 c.pipe(item); // 還有這 }); }); server.listen(8124, function() { //'listening' listener console.log('server bound'); });
我以爲整個結構沒什麼大問題,但當咱們趕上網絡情況很差的客戶端時,狀況就不大好了。這裏的兩個pipe
會把數據緩存在內存中,所以當客戶端不能及時接收數據時,這些數據就會大量滯留在內存當中。咱們每每假設客戶端的速度還不錯,但其實那都只是假設!
我想到的一個方法是,用一箇中間件來作緩存,當數據太多時使用文件緩衝,而數據很少則用內存,以下:
Delegate delegate; clientList.forEach(function(item){ delegate.append(item); // delegate內部會有一個與文件關聯的緩存 // 若是數據太大則把數據存入文件 });
若是你有其它的方案來處理這種狀況,不妨也分享一下:)
大多數小對象都是同步構造的,但對於某些封裝了複雜操做的對象來講,初始化都有多是異步的。
若是一個對象須要異步構造,那麼最好使用事件通知完成。這時你要留意官方文檔 中的一小段話:
This is important in developing APIs where you want to give the user the chance to assign event handlers after an object has been constructed, but before any I/O has occurred.
function MyThing(options) { this.setupOptions(options); process.nextTick(function() { this.startDoingStuff(); }.bind(this)); } var thing = new MyThing(); thing.getReadyForStuff(); // thing.startDoingStuff() gets called now, not before.
典型的解決方案是,在構造結束時用process.nextTick
來發消息:
function SomeTCPServer(options) { var self = this; // 其餘可能異步的初始化工做 process.nextTick(function(){ self.emit('ready'); }); } // 其餘代碼 var server = new SomeTCPServer(ops); server.on('ready', function when_ready(){ // 其它事情 });
由於用的是process.nextTick
,when_ready
不會錯過ready
事件。
我暫時就想起來這麼多,由於我本身也不是Node.js的真正專家。不過我仍是但願上面的幾條能對新手有所幫助,歡迎你們指出上面的任何錯漏,謝啦。