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);