Node.js是基于V8, 用于开发网络应用的一个平台, 它主要包含了基于TCP的异步操做和同步文件管理.javascript
Node.js使用的是非阻塞的I/O.java
EventEmitter: 事件APInode
简单来讲, 就是事件的发送接收机制. 例如在系统的某个地方, 咱们发送了一个start消息, 在另一个专门等待start信息的模块接收到这个信息后, 就开始处理事情.express
Node.js中的streams, networking和file system都基于EventEmitter.npm
var EventEmitter = require('events').EventEmitter; class MyEmitter extends EventEmitter {} var myEmitter = new MyEmitter(); myEmitter.on('show', () => { console.log('hello world'); }); myEmitter.emit('show');
Stream: 可扩展I/O编程
Stream是基于EventEmitter, 主要用于处理数据流.vim
使用Stream能够建立一个对象来接收所链接事件的数据: data用于新数据流入, end代表没有数据流入, 而error代表错误发生.网络
const fs = require('fs'); var input = fs.createReadStream(__filename); input.on('data', (chunk) => { if (chunk) { process.stdout.write(chunk); } }); input.on('end', () => { process.stdout.write('\nend!\n'); }); input.on('error', (err) => { if (err) throw err; });
使用pipe管道可简化代码:数据结构
var fs = require('fs'); var input = fs.createReadStream(__filename); var output = fs.createWriteStream('output.txt'); input.pipe(output);
不使用流处理:app
var fs = require('fs'); fs.readFile(__filename, (err, chunk) => { if (err) return console.error(err); if (chunk) process.stdout.write(chunk.toString()); });
FS: 文件操做
可同步或异步的读取文件.
NET: 用于建立网络客户端和服务端
基于HTTP协议, 用于操做网络.
当咱们安装第三方模块时, 咱们可使用npm.
lgtdeMacBook-Pro:test lgt$ cnpm install express
→ express@4.14.0 › type-is@1.6.14 (09:11:00)
→ express@4.14.0 › accepts@1.3.3 › mime-types@2.1.13 (05:39:28)
lgtdeMacBook-Pro:test lgt$ vim test.js
lgtdeMacBook-Pro:test lgt$ node test.js
function
lgtdeMacBook-Pro:test lgt$ cat test.js
var express = require('express');
console.log(typeof express);
这里使用了淘宝镜像cnpm, 安装则执行:
cnpm install xxx
默认安装在当前目录的node_modules下. 若是加上 -g 参数, 则默认全局安装, 通常在/usr/local/lib/node_modules下.
使用require("xxx")来加载模块.
经过module.exports加载当个文件的单个对象, 而exports加载单个文件的多个对象.
编程讲究模块思想, 咱们能够将代码分割成多个功能模块, 使用module.exports/exports进行加载.
myclass.js:
function MyClass() {} MyClass.prototype = { method: function() { return 'Hello'; } }; var myClass = new MyClass(); module.exports = myClass;
module-2.js:
exports.method = function() { return 'Hello'; }; exports.method2 = function() { return 'Hello again'; };
test.js:
var myClass = require('./myclass'); var module2 = require('./module-2'); console.log(myClass.method()); console.log(module2.method()); console.log(module2.method2());
运行程序, 则输出:
lgtdeMacBook-Pro:test lgt$ node test.js Hello Hello Hello again
由于存在require.cache, 因此不要担忧不断的require致使文件被重复的加载. 而require.resolve会解析出当前加载模块的具体文件路径, 若是想卸载某个功能模块, 咱们能够编写以下的代码:
delete require.cache(require.resolve('./myclass'));
加载一组文件状况下, 咱们能够编写index.js
在group目录下有三个文件:
one.js:
module.exports = function() { console.log('one'); };
two.js:
module.exports = function() { console.log('two'); };
index.js:
module.exports = { one: require('./one'), two: require('./two') };
test.js:
var group = require('./group'); group.one(); group.two();
运行程序, 则输出:
lgtdeMacBook-Pro:test lgt$ node test.js one two
在require一个目录时候, 默认会加载index.js文件.
使用__dirname/__filename来获取当前目录和文件
lgtdeMacBook-Pro:test lgt$ cat test.js console.log('__dirname:', __dirname); console.log('__filename:', __filename); lgtdeMacBook-Pro:test lgt$ node test.js __dirname: /Users/lgt/test __filename: /Users/lgt/test/test.js
从标准输入输出流(process.stdin/process.stdout)进行数据的读写
process.stdin.resume(); process.stdin.setEncoding('utf8'); process.stdin.on('data', function(text) { process.stdout.write(text.toUpperCase()); });
运行程序, 则输出:
lgtdeMacBook-Pro:test lgt$ cat process.js | node process.js PROCESS.STDIN.RESUME(); PROCESS.STDIN.SETENCODING('UTF8'); PROCESS.STDIN.ON('DATA', FUNCTION(TEXT) { PROCESS.STDOUT.WRITE(TEXT.TOUPPERCASE()); });
这里用到了管道, process.stdin为标准输入流, process.stdout为标准输出流.
使用console.time()/console.timeEnd()来统计运行时间
console.time('read'); var fs = require('fs'); var input = fs.createReadStream(__filename); var output = fs.createWriteStream('output.txt'); input.pipe(output); input.on('end', () => { console.timeEnd('read'); });
终端输出:
leicj@leicj:~/test$ node test.js read: 5.922ms
使用process.arch/process.platform获取操做系统和平台信息
lgtdeMacBook-Pro:test lgt$ node test.js process.arch: x64 process.platform: darwin
使用process.argv传递命令行参数
lgtdeMacBook-Pro:test lgt$ node test.js a b c d process.argv: [ '/usr/local/bin/node', '/Users/lgt/test/test.js','a','b','c','d' ]
使用process.exit(status_code)传递状态码并退出程序
使用process来接收信号
process.stdin.resume(); process.on('SIGINT', () => { console.log('Reloading configuration...'); }); console.log('PID:', process.pid);
运行终端:
leicj@leicj:~/test$ node test.js PID: 7203 ^CReloading configuration... ^CReloading configuration... ^CReloading configuration... ^CReloading configuration... ^CReloading configuration... ^CReloading configuration... ^CReloading configuration...
默认状况下, Node.js的核心功能模块都是返回buffer的.
将buffer转换为其余数据结构
使用Buffer的toString方法, 将buffer转换为默认的UTF-8.
var fs = require('fs'); fs.readFile(__filename, (err, chunk) => { if (err) return console.error(err); console.log(chunk); console.log(chunk.toString()); });
备注: 这里不能使用process.stdout.write, 不然输出字符串. 考虑如下代码:
> var buf = Buffer.from('hello') undefined > buf + '' 'hello' > buf <Buffer 68 65 6c 6c 6f>
若是在toString()中传递参数, 则将buffer转换为其它数据结构, 也能够经过new Buffer(str, 'xxx')将其它数据结构转换为buffer.
> var s = "hello" undefined > var s1 = new Buffer(s).toString('base64') undefined > s1 'aGVsbG8=' > new Buffer(s1, 'base64').toString() 'hello'
从EventEmitter中继承
EventEmitter中存在两个基本函数: on用于接收信号, 而emit用于发射信号.
var EventEmitter = require('events').EventEmitter; class MyEmitter extends EventEmitter {} var myEmitter = new MyEmitter(); myEmitter.on('show', () => { console.log('show'); }); myEmitter.on('show', () => { console.log('show again'); }); myEmitter.emit('show');
终端输出:
lgtdeMacBook-Pro:test lgt$ node test.js show show again
EventEmitter提供两个函数用于删除listener, emitter.removeListener用于删除特定的的事件, 而emitter.removeAllListeners用于删除全部的事件.
而对于emitter.removeListener来讲, 须要提供第二个参数: 事件中执行的函数名.
var EventEmitter = require('events').EventEmitter; class MyEmitter extends EventEmitter {} var myEmitter = new MyEmitter(); function f1() { console.log('f1'); } function f2() { console.log('f2'); } myEmitter.on('show', f1); myEmitter.on('show', f2); myEmitter.removeListener('show', f1); myEmitter.emit('show');
运行程序, 则输出:
lgtdeMacBook-Pro:test lgt$ node test.js f2
正常状况下, 若是程序发生异常, 则会将异常信息打印在终端, 而且终止程序.
但咱们能够捕获异常信息:
var EventEmitter = require('events').EventEmitter; class MyEmitter extends EventEmitter {} var myEmitter = new MyEmitter(); myEmitter.on('show', () => { myEmitter.emit('error', 'unable to show'); }); myEmitter.on('error', (err) => { console.error('Error:', err); }); myEmitter.emit('show');
运行程序, 输出:
lgtdeMacBook-Pro:test lgt$ node test.js Error: unable to show
若是存在多个EventEmitter对象, 甚至存在嵌套的对象, 每一个对象都会emit一个错误信息, 则最好使用domain来处理异常.
var d = require('domain').create(); d.on('error', (er) => { console.log('error, but oh well', er.message); }); d.run(() => { require('http').createServer((req, res) => { handleRequest(req, res); }).listen(PORT); });
这时候, 若是运行程序, 则提示以下的错误:
error, but oh well PORT is not defined
假设存在一种状况, 咱们须要监听一个listener加入到EventEmitter, 或者查看全部的listeners.
EventEmitter提供一个特殊的事件: newListener
var EventEmitter = require('events').EventEmitter; class MyEmitter extends EventEmitter {} var myEmitter = new MyEmitter(); myEmitter.on('newListener', (name, listener) => { console.log('Event name added:', name); }); myEmitter.on('a listener', () => {});
运行程序后输出:
Event name added: a listener
咱们可使用listeners获取全部的监听事件(针对同一个name):
var EventEmitter = require('events').EventEmitter; class MyEmitter extends EventEmitter {} var myEmitter = new MyEmitter(); myEmitter.on('a listener', () => {}); myEmitter.on('a listener', () => {}); // 2 console.log(myEmitter.listeners('a listener').length);
在一个大型的系统中, 例如不一样的功能模块若是要进行通讯, 咱们也能够经过EventEmitter来实现:
var express = require('express'); var app = express(); app.on('hello-alert', function() { console.warn('warning!'); }); app.get('/', function(req, res) { res.app.emit('hello-alert'); res.send('hello world'); }); app.listen(3000);