摘要:不知不覺(jué)的已經(jīng)用有將近一年了,這里我有幾條出自實(shí)踐的建議送給剛剛?cè)腴T的的朋友。命名而不是匿名在中,我們可以創(chuàng)建匿名對(duì)象和匿名函數(shù)。一般來(lái)說(shuō),匿名函數(shù)可以讓代碼更加簡(jiǎn)短精悍。然而,對(duì)這些對(duì)象或函數(shù)進(jìn)行命名,則有利于調(diào)試和優(yōu)化。
不知不覺(jué)的已經(jīng)用Node.js有將近一年了,這里我有幾條出自實(shí)踐的node.js建議送給剛剛?cè)腴T的node.js的朋友。
命名而不是匿名在JavaScript中,我們可以創(chuàng)建匿名對(duì)象和匿名函數(shù)。一般來(lái)說(shuō),匿名函數(shù)可以讓代碼更加簡(jiǎn)短精悍。
然而,對(duì)這些對(duì)象或函數(shù)進(jìn)行命名,則有利于調(diào)試和優(yōu)化。以下是我從Chrome DevTool的文章中借用的圖片:
很明顯,命名的實(shí)體更利于調(diào)試和優(yōu)化。
盡早解引用JavaScript的GC以一種類似引用計(jì)數(shù)的算法工作,一個(gè)對(duì)象當(dāng)且僅當(dāng)所有指向它的引用全部釋放之后,它本身才會(huì)被釋放掉。
然而可能你有其他的閉包或者全局對(duì)象持有它的引用,從而阻止了垃圾回收。為避免這一現(xiàn)象,應(yīng)當(dāng)盡早地解除不必要的引用:
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...
如果閉包中尚有一個(gè)變量未能被釋放,那么整個(gè)閉包都有可能無(wú)法被回收,從而造成其它對(duì)象也無(wú)法釋放。
現(xiàn)在有很多工具可以輔助我們監(jiān)視內(nèi)存的變化情況,比如heapdump、webkit-devtools-agent等等。同時(shí),這也更加說(shuō)明了為什么要盡可能使用命名而不是匿名的對(duì)象。
別復(fù)制代碼我用Node.js開(kāi)發(fā)的項(xiàng)目代碼變化非??欤貥?gòu)也很頻繁,常常會(huì)到處復(fù)制代碼。然而每當(dāng)我這樣做的時(shí)候,后來(lái)都會(huì)發(fā)現(xiàn)某些變量要么就是忘了改名,要么就是有些變量壓根就不存在了,導(dǎo)致代碼變得極為不穩(wěn)定。
JS是一門高度動(dòng)態(tài)的語(yǔ)言,它不像C++這樣可以利用編譯器做靜態(tài)檢查,因此你幾乎沒(méi)有機(jī)會(huì)依靠工具檢查出這些問(wèn)題。所以我的建議是,盡可能地自己再輸入一遍代碼,這樣IDE或者編輯器有機(jī)會(huì)利用代碼自動(dòng)補(bǔ)全來(lái)為你檢查。如果你的IDE還沒(méi)這功能,那你真得考慮換掉它了。
慎重引入新模塊Node.js社區(qū)非?;钴S,有成千上萬(wàn)的現(xiàn)成模塊可以取用,然而其中有些其實(shí)已經(jīng)沒(méi)人管了。Node.js的API也常常發(fā)生變化,適配node v0.8.x的模塊,有可能不支持v0.10.x。
因此當(dāng)你考慮引入新的模塊的時(shí)候,務(wù)必先去它的pull request列表或者issue列表看看,確認(rèn)一下這個(gè)模塊是不是已經(jīng)被拋棄,或者存在重大的隱患。
用async.js或者promiseNode.js基于回調(diào)。因?yàn)榛卣{(diào)的本質(zhì),我們很容易寫(xiě)出嵌套多層的回調(diào)函數(shù)?;卣{(diào)對(duì)于異步來(lái)說(shuō)是好事,但對(duì)于代碼維護(hù)來(lái)說(shuō)卻是壞事。
如果你發(fā)現(xiàn)代碼需要3層以上的回調(diào)函數(shù)嵌套,那你應(yīng)該考慮一下,要不要使用async.js或者基于promise概念的模塊。
以下是一個(gè)用async.js寫(xiě)出來(lái)的一系列異步操作:
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與數(shù)據(jù)庫(kù)無(wú)關(guān) "open_cache": ["load_config", function(done){ open_cache_here(done); }], // warm_up通常用于從數(shù)據(jù)庫(kù)預(yù)先抓取一些數(shù)據(jù) // 注意,warm_up只會(huì)在"init_database"以及"open_cache"結(jié)束后執(zhí)行 "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){ // 在這處理異常 } });
我對(duì)promise相關(guān)的模塊不是很熟,但使用Q,應(yīng)該可以寫(xiě)出這樣的代碼,,同樣易于閱讀:
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();
選取什么樣的庫(kù)來(lái)簡(jiǎn)化邏輯一般都是隨便你。通常來(lái)說(shuō),async.js非常簡(jiǎn)單,而Q則更加靈活強(qiáng)大。
比如說(shuō)async.js中各個(gè)函數(shù)獨(dú)立而不嵌套,因此如果你想通過(guò)捕獲某個(gè)函數(shù)中的變量就顯得有些困難,而在Q中就可以使用嵌套的then語(yǔ)句。
本來(lái)我還想寫(xiě)一寫(xiě)不使用任何輔助模塊的上述代碼,但其實(shí)我都很懷疑自己能不能寫(xiě)對(duì)。
另外,你是CoffeeScript的擁泵么?如果是,那你還可以嘗試一下IcedCoffeeScript。IcedCoffeeScript在語(yǔ)言層面上提供了兩個(gè)非常有用的關(guān)鍵字: await和defer?;旧纤械牧鞒炭刂贫伎梢酝ㄟ^(guò)這兩個(gè)關(guān)鍵字進(jìn)行改造。不過(guò)我本人對(duì)IcedCoffeeScript并不熟悉,所以就不在這獻(xiàn)丑了。這里感謝brettof86向我介紹了這一利器,不過(guò)我還需要花些時(shí)間來(lái)熟悉它。
客戶端也許特別慢用Node.js的時(shí)候我們可能會(huì)變得有點(diǎn)過(guò)于理想化了,因此很容易寫(xiě)出下面這樣的聊天室代碼:
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"); });
我覺(jué)得整個(gè)結(jié)構(gòu)沒(méi)什么大問(wèn)題,但當(dāng)我們遇上網(wǎng)絡(luò)狀況不好的客戶端時(shí),情況就不大好了。這里的兩個(gè)pipe會(huì)把數(shù)據(jù)緩存在內(nèi)存中,因此當(dāng)客戶端不能及時(shí)接收數(shù)據(jù)時(shí),這些數(shù)據(jù)就會(huì)大量滯留在內(nèi)存當(dāng)中。我們往往假設(shè)客戶端的速度還不錯(cuò),但其實(shí)那都只是假設(shè)!
我想到的一個(gè)方法是,用一個(gè)中間件來(lái)做緩存,當(dāng)數(shù)據(jù)太多時(shí)使用文件緩沖,而數(shù)據(jù)不多則用內(nèi)存,如下:
Delegate delegate; clientList.forEach(function(item){ delegate.append(item); // delegate內(nèi)部會(huì)有一個(gè)與文件關(guān)聯(lián)的緩存 // 如果數(shù)據(jù)太大則把數(shù)據(jù)存入文件 });
如果你有其它的方案來(lái)處理這種情況,不妨也分享一下:)
用事件通知完成,而且不要太早大多數(shù)小對(duì)象都是同步構(gòu)造的,但對(duì)于某些封裝了復(fù)雜操作的對(duì)象來(lái)說(shuō),初始化都有可能是異步的。
如果一個(gè)對(duì)象需要異步構(gòu)造,那么最好使用事件通知完成。這時(shí)你要留意官方文檔 中的一小段話:
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.
典型的解決方案是,在構(gòu)造結(jié)束時(shí)用process.nextTick來(lái)發(fā)消息:
function SomeTCPServer(options) { var self = this; // 其他可能異步的初始化工作 process.nextTick(function(){ self.emit("ready"); }); } // 其他代碼 var server = new SomeTCPServer(ops); server.on("ready", function when_ready(){ // 其它事情 });
因?yàn)橛玫氖?b>process.nextTick,when_ready不會(huì)錯(cuò)過(guò)ready事件。
其它建議?我暫時(shí)就想起來(lái)這么多,因?yàn)槲易约阂膊皇荖ode.js的真正專家。不過(guò)我還是希望上面的幾條能對(duì)新手有所幫助,歡迎大家指出上面的任何錯(cuò)漏,謝啦。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/77942.html
摘要:中文資料導(dǎo)航官網(wǎng)七牛鏡像深入淺出系列進(jìn)階必讀中文文檔被誤解的編寫(xiě)實(shí)戰(zhàn)系列熱門模塊排行榜,方便找出你想要的模塊多線程,真正的非阻塞淺析的類利用編寫(xiě)異步多線程的實(shí)例中與的區(qū)別管道拒絕服務(wù)漏洞高級(jí)編程業(yè)界新聞看如何評(píng)價(jià)他們的首次嘗鮮程序員如何說(shuō)服 node.js中文資料導(dǎo)航 Node.js HomePage Node官網(wǎng)七牛鏡像 Infoq深入淺出Node.js系列(進(jìn)階必讀) Nod...
摘要:是指可能導(dǎo)致程序終止的非常嚴(yán)重的時(shí)間。具有最高的級(jí)別,旨在關(guān)閉中的日志功能。因此為每一個(gè)消息選擇一個(gè)合適的日志級(jí)別是非常重要的。日志的個(gè)小建議將日志訪日代碼塊它能顯著的減少因?yàn)樽址唇佣鴰?lái)的性能的影響。 前言 首先,這篇文章沒(méi)有進(jìn)行任何的日志功能的詳細(xì)介紹,而是對(duì)日志提出了幾種最佳實(shí)踐。適合對(duì)日志記錄有所了解的同學(xué)閱讀。下面是正文: JAVA日志管理既是一門科學(xué),又是一門藝術(shù)。科學(xué)...
摘要:最近剛好有在研究銀聯(lián)云閃付的支付模塊,所以就寫(xiě)篇總結(jié)分享給大家。很方便的就是,銀聯(lián)云閃付的接入給我們準(zhǔn)備了測(cè)試模式,就是你并不需要真的有商戶號(hào)才能開(kāi)發(fā),不像微信支付那樣非要有商戶號(hào)才能測(cè)試開(kāi)發(fā)。 你好,是我琉憶。最近剛好有在研究銀聯(lián)云閃付的支付模塊,所以就寫(xiě)篇總結(jié)分享給大家。 這算是第二次接觸支付的東西了,接觸得最多的是接入微信支付,自己也有相關(guān)的總結(jié)文章,可以去segmentfaul...
閱讀 3259·2023-04-25 19:09
閱讀 3965·2021-10-22 09:54
閱讀 1832·2021-09-29 09:35
閱讀 2995·2021-09-08 09:45
閱讀 2428·2021-09-06 15:00
閱讀 2837·2019-08-29 15:32
閱讀 1117·2019-08-28 18:30
閱讀 424·2019-08-26 13:43