Node.js中所有的异步的I/O操作在完成时都会发送一个事件到事件队列。事件由EventEmitter对象提供。
Node.js的事件循环:
node.js程序由事件循环开始,到事件循环借宿,所有的逻辑都是事件的回调函数。node.js始终在事件循环中,程序入口就是事件循环第一个事件的回调函数。
事件回调函数在执行过程中可能会发出I/O请求或直接发射(emit)事件,执行完毕后再返回事件循序,事件循环会检查事件队列中有没有未处理的事件,直到程序结束。
node.js的事件循环由libev库实现。libev支持多种类型的事件,eg: ev_io,ev_timer,ev_signal,ev_idle。。。均被封装EventEmitter封装。
模块(Module 和 Package)和包:
包可以理解为是实现了某个功能模块的集合,用于发布和维护。
模块: 文件和模块是一一对应的,一个Node.js文件就是一个模块,这个文件可能就是JavaScript代码,JSON或者编译过的C/C++扩展。
1.创建模块:
exports是模块公开的接口,require用于从外部获取一个模块的接口,即所获取模块的exports对象。
创建一个A.js文件,用exports.functionName的函数,在另一个B.js中用require('A的相对路径')以后可是直接使用,但是在A中如果有函数没有通过exports实现,则在B中无法调用。
exports是模块的公开接口,require用于从外部获取一个模块的接口,即获取模块的exports对象。
node.js的单次加载特性,
var hello1 = require("./module"); hello1.setName('1'); var hello2 = require("./module"); hello2.setName('2'); hello1.sayHello(); 结果是 Hello 2
因为hello1和hello2都指向同一个实例。
2. 如果只是需要把一个对象封装到模块中,则可以通过: module.exports = hello;(在A.js中)
然后在B.js中可以通过 hello = new Hello(); 来new一个对象出来。
exports本身不仅仅是一个普通的空对象,即{},它专门用来声明接口,本质上是通过它为模块闭包的内部机建立了一个有限的访问接口。因为它没有任何特殊的地方,所以可以用其他东西来替代。
注: 不可以通过对exports直接赋值代替对module.exports赋值。exports实际上只是一个和module.exports指向同一个对象的变量,但是它本身会在模块执行结束后释放,但module不会,因此只能通过指定module.exports来改变访问接口。
符合CommonJS规范的包应该具备以下特征:
1.package.json必须在包的顶层目录下;
2.二进制文件应该在bin目录下
3.JavaScript代码应该在lib目录下
4.文档应该在doc目录下
5.单元测试应该在test目录下
包通常是一些模块的集合,在模块的基础上提供了更高层的抽象,相当于提供了一些固定接口的函数库。通过定制package.json,可以创建更复杂、更完善、更符合规范的包用于发布。
Node.js在调用某个包时,会首先检查保重package.json文件的main字段,将其作为包的接口模块,如果package.json或main字段不存在,会尝试寻找index.js或index.node作为包的接口。
完全符合规范的package.json文件应该含有以下字段:
1.name : 包的名称,必须是唯一的,由小写英文字母,数字,下划线组成,不能包含空格。
2.description : 包的简要说明。
3.version : 符合语义化版本识别规范的版本字符串。
4.keywords : 关键字数组,通常用于搜索。
5.maintainer : 维护者数组,每个元素要包含name , email(可选) , web(可选)字段。
6.contributors : 贡献者数组,格式与maintainers相同。包的作者应该是贡献者数组的第一个元素。
7.bugs : 提交bug的地址,可以使网址或者电子邮件地址。
8.licenses : 许可证数组,每个元素要包含type(许可证的名称)和url(链接到许可证问本地的地址)字段。
9.repositories : 仓库托管地数组,每个元素要包含type(仓库的类型,eg : git )、url(仓库的地址)和path(相对于仓库的路径,可选)字段。
10.dependencies : 包的依赖,一个关联数组,由包名称和版本号组成。