不知不觉的已经用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的真正专家。不过我仍是但愿上面的几条能对新手有所帮助,欢迎你们指出上面的任何错漏,谢啦。