Hello Fuck以下:javascript
console.log('Hello, Fuck you, NodeJs'); # node Helloworld.js Hello, Fuck you, NodeJs
事件:html
Node.js全部的异步i/o操做在完成时都会发送一个事件到事件队列,事件由EventEmitter对象来提供,前面提到的fs.readFile和http.createServer的回调函数都是经过EventEmitter来实现的。java
//event.js var EventEmitter = require('events').EventEmitter; var event = new EventEmitter(); event.on('some_event', function() { console.log('some_event occurred.'); setTimeout(function(){ event.emit('some_event'); },1000); }); setTimeout(function() { event.emit('some_event'); },1000); console.log('end?'); # node event.js end? some_event occurred. some_event occurred. some_event occurred. ^C
nodejs不停监测是否有活动的事件监听器好比i/o, timer等,一旦发现没有活动的事件监听器,nodejs进程将退出。node
模块c++
模块是Node.js的基本组成部分,文件和模块是一一对应的,换言之,一个Node.js文件就是一个模块,这个文件多是JavaScript代码,JSON或者编译过的c/c++扩展。git
前面章节的例子中,咱们曾经用到了相似于var http=require('http'),其中http就是一个核心模块,其内部是用c++来实现的,外部使用javascript来进行封装。经过require函数获取了这个模块以后,而后才能使用其中的对象。github
建立,加载模块web
在Node.js中,建立一个模块很是单简单,由于一个文件就是一个模块,咱们关注的问题仅仅在于如何在其它文件中获取这个模块,Node.js提供了exports和require两个对象,其中exports是模块公开的接口,而require用于从外部获取一个模块的接口,也就是获取返回的exports对象。express
// module.js var name; exports.setName=function(theName) { name=theName; } exports.sayHello=function() { console.log('Fuck you ' + name); } // getModule.js var myModule=require('./module') //注意这里须要./前缀,由于是相对当前工做目录的。 myModule.setName('Mosmith'); myModule.sayHello(); # node getModule.js Hello Mosmith
单次加载,这个有点相似于建立一个对象,但实际上和对象又有本质的区别,由于require不会重复加载模块,也就是说不管调用多少次require,得到的模块都是同一个。npm
覆盖exports,有时候咱们只是想将一个对象封闭到一个模块中,例如:
// module.js function Hello() { var name; this.setName=function(_name) { this.name=_name; } this.sayHello=function() { console.log("Hello " + this.name); } } exports.Hello=Hello; // override exports object //module.exports=Hello;
// getModule.js
var Hello = require('./module.js').Hello;
var hello=new Hello();
hello.setName('Mosmith');
hello.sayHello();
须要注意的是,不能够经过对 exports 直接赋值代替对 module.exports 赋值。exports 实际上只是一个和 module.exports 指向同一个对象的变量,它自己会在模块执行结束后释放,但 module 不会,所以只能经过指定module.exports 来改变访问接口。
包是在模块的基础上更深一步的抽像,Node.js的包相似于c/c++的函数库或者Java/.Net的类库,它将某个独立的功能封装起来,用于发布,更新,依赖管理和版本控制,Node.js根据CommonJS规范实现了包机制,开发了npm来解决包的发布和获取需求。
Node.js的包是一个目录,其中包含了一个JSON格式的说明文件,package.json,严格符合CommonJS的包应该具有如下特征。
但Node.js对包的要求没有这么严格,只要顶层目录下面有package.json,并符合一些规范便可。但最好是符合规范。Node.js在调用某个包是,先会去检查包中的package.json中的main字段,将其做为包的接口模块,若是package.json或者main字段不存在,那么会尝试将寻找index.js或者index.node做为包的接口。
package.json是CommonJS规定用于描述包的文件,完整的包应该含有如下字段:
name:包的名称,必须是惟一的由小写字母,数字,和下划线组成,不能有空格。
description:包的简要说明。
version:符合语义化版本识别规范的字符串
maintainer:维护者数组,第个元素要包含name, email(可选), web(可选)
contributors:贡献者数组,格式与maintainer相同,包的做者应该是都数组的第一个元素。
bugs提交bug的地址。
licenses许可证数组,每一个元素要包含type的url
repositories仓库托管地址数组,每一个元素要包含type(仓库的类型好比git),url(仓库的地址)和path(相对于仓库的路径,可选)字段。
dependencies:包的依赖,一个关联数组,由包的名称和版本号组成。
下面是一下符合CommonJS规范的package.json示例:
{ "name": "mypackage", "description": "Sample package for CommonJS. This package demonstrates the required elements of a CommonJS package.", "version": "0.7.0", "keywords": [ "package", "example" ], "maintainers": [ { "name": "Bill Smith", "email": "bills@example.com", } ], "contributors": [ { "name": "BYVoid", "web": "http://www.byvoid.com/" } ], "bugs": { "mail": "dev@example.com", "web": "http://www.example.com/bugs" }, "licenses": [ { "type": "GPLv2", "url": "http://www.example.org/licenses/gpl.html" } ], "repositories": [ { "type": "git", "url": "http://github.com/BYVoid/mypackage.git" } ], "dependencies": { "webkit": "1.2", "ssl": { "gnutls": ["1.0", "2.0"], "openssl": "0.9.8" } } }
npm是Node.js的包管理工具,它已经成为Node.js包的标准发布平台。用于Node.js包的发布,传播,依赖控制。
npm install/i package_name好比要安装express
npm install express或者npm i express
在项目目录下运行npm install 将下载并安装package.json中的依赖。
同时npm还会自动解析其依赖,并获取express依赖的mime,mkdirp,qs的connect等。
注意:express4.x中将命令工具分离出来了,全部须要先装express-generator
在新建一个nodejs工程的时候咱们可使用npm init命令来进行初始化package.json,它的功能相似于maven archetype:generate
本地模块与全局模式
默认状况下npm会从http://npmjs.org搜索并下载包,并将包安装到当前目前的node_modules子目录下面。也就是本地模式。
另外npm能够以全局模式安装(使用-g参数),使用方法为:
npm install/i -g packageName
但须要注意的是,全局模式下可能会形成冲突,由于别的nodejs程序可能须要另外版本的包。
本地模式下npm不会注册环境变量,而全局模式下会注意环境变量,并将包安装到系统目录好比/usr/local/lib/node_modules,同时package.json文件中的bin字段包含的文件会被连接到/usr/local/bin。/usr/local/bin是在PATH环境变量中默认定义的,所以就能够直接在命令中运行像supervisor的模块了。但全局模式安装的package不能经过require来使用,由于require不会去搜索/usr/local/lib/node_modules目录。
建立全局连接
npm提供了一个npm link命令,它的功能是在本地包和全局包之间建立符号连接,咱们说过使用全局模式安装的包不能经过require来使用,但经过npm link命令能够绕过这个限制。好比:
npm link express
这里能够在node_modules子目录发现一个安装到全局的包的符号连接。但这个命令不能在windows下来使用。
调试。
node debug debug.js
node --debug[=port] script.js 而后在另外一个终端node debug localhost:debug_port或者使用IDE来进行远程调试。
全局对象
JavaScript中有一个特殊的对象,称为全局对象,它全部的属性能够在程序的任何地方访问,也就是说全局变量,在浏览器的JavaScript中,一般window是全局对象,而Node.js中的全局对象是global,全部的全局变量(除了global自己之外),都是global对象的属性。咱们在Node.js中可以直接访问到对象经过都是global的属性好比console, process等。
全局对象与全局变量
global最根本的做用是做为全局变量的宿主,按照ECMAScript的定义,知足如下条件的是全局变量。
当你定义一个全局变量时,这个变量同时也会成为全局对象的属性,反之亦然。须要注意的是,在 Node.js 中你不可能在最外层定义变量,由于全部用户代码都是属于当前模块的,而模块自己不是最外层上下文,提倡永远使用 var 定义变量以免引入全局变量,由于全局变量会污染命名空间,提升代码的耦合风险。
process
process是一个全局变量,即global对象的一个属性,它用于描述当前Node.js进程的状态,提供了一个与操做系统的简单接口。写一些本地命令行的程序的时候常常须要和它打交道的。
process.argv是命令行参数数组,第一个元素是node,第二个元素是脚本文件名,从第三个元素开始每一个元素是一个运行参数。
process.stdout是标准输出流,一般咱们使用的console.log向标准输出打印字符,而process.stdout.write()函数则提供更加底层的接口。
process.stdin是标准输入流,初始时它是被暂停的,想要从标准输入读取数据你必须恢复流,并手动编写流的事件响应函数。好比下面:
process.nextTick(callback)的功能是为事件循环设置一项任务,Node.js会在下一次事件循环时调用callback。Node.js 适合 I/O 密集型的应用,而不是计算密集型的应用,由于一个 Node.js 进程只有一个线程,所以在任什么时候刻都只有一个事件在执行。若是这个事件占用大量的 CPU 时间,执行事件循环中的下一个事件就须要等待好久,所以 Node.js 的一个编程原则就是尽可能缩短每一个事件的执行时间。 process.nextTick() 提供了一个这样的工具,能够把复杂的工做拆散,变成一个个较小的事件。
process.stdin.on('data', function(data) { process.nextTick(function() { process.stdout.write('do something very time-consuming'); process.stdout.write(data); }); });
须要setTimeout也能够达到相似的做用,但setTimeout效率很低,回调不能被及时执行。
除了上面几个比较经常使用的成员,除些以后有process.platform,process.pid,process.execPath,process.memoryUsage(),以及POSIX进程信号响应机制。
console
console用于提供控制台标准/错误输出:
console.log()向标准输出流打印字符并以换行符结束。console.log接受若干个参数,若是只有一个参数,则输出这个参数的字符串形式,若是有多个参数,则相似于c语言的printf命令的格式化输出。
console.log("Helloworld"); => Helloworld
console.log("Helloworld%s"); => Helloworld%s console.log("Helloworld %s", Mosmith); => Helloworld Mosmith
console.error()与console.log相同,只不过console.error()向标准错误流输出。
console.trace()用于向标准错误流输出当前的调用栈。
经常使用工具util
util是Node.js的核心模块,提供经常使用函数的集合,用于弥补核心JavaScript的功能过于精简的不足。
util.inherits(constructor, superConstructor)是一个实现对象间原型继承的函数,JavaScript的面向对象我是基于原型的,与常见的基于类不一样,JavaScript并无提供对象继承的语言级别特性,而是经过原型复制来实现的,具体细节咱们在附录A中说明,这里咱们只介绍util.inerits的用法,示例以下:
var util=require('util') function Base() { this.name='base'; this.base=1991; this.sayHello = function() { console.log('Hello ' + this.name); } } Base.prototype.showName=function() { console.log(this.name); } function Sub() { this.name='sub'; } util.inherits(Sub,Base); var objBase=new Base(); objBase.showName(); objBase.sayHello(); console.log(objBase); var objSub=new Sub(); objSub.showName(); // This is undefined in Sub // objSub.sayHello(); console.log(objSub);
util.inspect
util.inspect(object, [showHidden],[depth],[colors])是一个将任意对象转换为字符串的方法,一般用于调试和错误输出,它至少一个参数object,即要转换的对象,showHidden是一个可选的参数,若是值为true,将会输出更多的参数,depth表示最大的递归层数,若是对象很复杂,你能够指定层数以控制输出信息的多少,默认状况下递归两层,为null的状况下则不限层数。
事件驱动events
events是Node.js的最重要的模块,Node.js自己就是依赖于event实现事件驱动的,而它提供了惟一的接口。events模块不只仅用于用户代码与Node.js下层事件循环的交互,还几乎被全部的模块依赖。
事件发射器
events模块只提供了一个对象,events.EventEmitter, EventEmitter的核心就是事件发射与事件监听器功能的封装。EventEmitter的每个事件由一个事件和若干个参数组成,事件名是一个字符串,一般表达必定的语义,对于每一个事件,EventEmitter支持若干个事件监听器,当事件发射时,注册到这个事件的事件监听器将被依次调用,事件参数做为回调函数传递。
// eventEmitterTest.js
var events=require('events'); var emitter=new events.EventEmitter(); emitter.on('someEvent',function(arg1,arg2){ console.log('listener1 invoked',arg1,arg2); }); emitter.on('someEvent', function(arg1,arg2) { console.log('listener2 invoked',arg1,arg2); }); emitter.emit('someEvent','argument1',2017); $ node eventEmitterTest.js listener1 invoked argument1 2017 listener2 invoked argument1 2017
error事件
EventEmitter定义了一个特殊的事件error,它包含了’错误‘的语义,咱们在遇到异常的时候一般会发射error事件。当error被发射时,EventEmitter规定若是没有响应的监听器,那么Node.js会将它看成异常,退出程序并打印调用栈。所以咱们通常要为会发射error事件的对象设置监听器,避免遇到错误后整个程序崩溃。好比:
var events = require('events'); var emitter = new events.EventEmitter(); emitter.emit('error'); 运行时会显示如下错误: node.js:201 throw e; // process.nextTick error, or 'error' event on first tick ^ Error: Uncaught, unspecified 'error' event. at EventEmitter.emit (events.js:50:15) at Object.<anonymous> (/home/byvoid/error.js:5:9) at Module._compile (module.js:441:26) at Object..js (module.js:459:10) at Module.load (module.js:348:31) at Function._load (module.js:308:12) at Array.0 (module.js:479:10) at EventEmitter._tickCallback (node.js:192:40)
继承EventEmitter
大多数时候咱们不会直接使用 EventEmitter ,而是在对象中继承它。包括 fs 、 net 、http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类。为何要这样作呢?缘由有两点。首先,具备某个实体功能的对象实现事件符合语义,事件的监听和发射应该是一个对象的方法。其次 JavaScript 的对象机制是基于原型的,支持部分多重继承,继承 EventEmitter 不会打乱对象原有的继承关系。
文件系统fs
fs模块是文件操做的封闭,它提供了文件的读取,写入,改名,删除,遍历目录,连接等POSIX文件操做,与其它模块不一样的是,fs模块全部的操做都有同步和异步两个版本,例如读取文件内容的函数有异步的fs.readFile()也有同步的fs.readFileSync
fs.readFile(filename, [encoding], [callback(err,data)])是最简单的读取文件的函数,它接受一个必选的参数filename,若是不提供encoding,那么将以二进制方式打开,若是指定了encoding,data是一个解析后的字符串。
fs.readFIleSync是同步版本,它没有callback参数,data经过返回值获取,而错误则须要经过try catch来进行捕捉处理。
fs.open(path, flag, [mode], [callback(err,fd])]是POSIX open函数的封装,与c和fopen相似,它两个必选参数,path为文件路径,flag是表示以什么方式打开文件,mode参数用于建立文件时给文件指定权限,默认是0666,回调函数将传递一个错误参数,以及一个文件描述符fd
fs.read(fd,buffer, offset, length, position, callback(err, byteRead, buffer)]是POSIX read函数的封闭,相比于fs.readFile, 它提供了更加底层的接口,fs.read功能是从指定的文件描述符fd中读取数据并写入buffer指定的缓冲区对象,offset是buffer的写入偏移量,length是要从文件中读取的字节数,position是文件读取的起始位置,若是position的值为null,则会从当前文件指针的位置读取,回调函数传递byteRead和buffer,分别表示读取的字节数和缓冲区对象。
其中有两个全局变量:
__dirname:全局变量,存储的是文件所在的文件目录
__filename:全局变量,存储的是文件名
http模块
Node.js标准库里面提供了http模块,其中Node.js 标准库提供了 http 模块,其中封装了一个高效的 HTTP 服务器和一个简易的HTTP 客户端。 http.Server 是一个基于事件的 HTTP 服务器,它的核心由 Node.js 下层 C++部分实现,而接口由 JavaScript 封装,兼顾了高性能与简易性。 http.request 则是一个HTTP 客户端工具,用于向 HTTP 服务器发起请求,例如实现 Pingback 1 或者内容抓取
http.Server
http.Server是http模块中的HTTP服务器对象,用Node.js作的全部基于HTTP协议的系统如网站,社交应用,甚于代理服务器,都是基于http.Server来实现的,它提供了一套封装级别很低的API,仅仅是流控制和简单的消息解析,全部的高层功能都要经过它的接口来实现。