JavaScript on the Serverhtml
JavaScript was originally built for web browsers, but with Node.js we can use it on the server.前端
We can perform server-related tasks like file system access.node
JavaScript是前端開發的惟一標準。git
2000年後開始的瀏覽器平臺大戰,致使了node的誕生。es6
在2009年,Ryan(人)正式推出了基於JavaScript語言和V8引擎的開源Web服務器項目,命名爲Node.js。github
選擇js,是由於它是單線程,只能使用異步IO。web
Node第一次把JavaScript帶入到後端服務器開發,加上世界上已經有無數的JavaScript開發人員,因此Node一會兒就火了起來。算法
最大的優點是藉助JavaScript天生的事件驅動機制加V8高性能引擎,使編寫高性能Web服務垂手可得。chrome
其次,JavaScript語言自己是完善的函數式語言,在前端開發時,開發人員每每寫得比較隨意,讓人感受JavaScript就是個「玩具語言」。可是,在Node環境下,經過模塊化的JavaScript代碼,加上函數式編程,而且無需考慮瀏覽器兼容性問題,直接使用最新的ECMAScript 6標準,能夠徹底知足工程上的需求。express
(見以前的博客)
安裝成功後使用node -v和npm -v查看版本。
在終端輸入node, 進入Node.js的交互環境。能夠輸入任何js語句。
退出.exit,或者按兩次ctrl+c.
選擇偶數的版本,node核心團隊維護這類版本的時間達數年之久。具體須要看官網/git(👆的鏈接)
奇數版本,屬於實驗性版本,維護時間1年左右。
維護指:security vulnerability, patches(補丁)
再看視頻(2016版本使用express+ MongoDB)
學習Koa(文章)(廖雪峯上也有)
視頻(YoutTube):https://www.youtube.com/watch?v=PT_-u2fFTaI&list=PLguYmmjtxbWHY2vCHIkugUpNdzE3QNOvf&index=4&t=0s
//使用mac自帶的vim編輯器,也可使用atom等 vi hello.js //而後輸入js代碼,保存 //在所存文件的文件夾下,輸入 node hello.js //在terminal上, 顯示consol.log()打印的代碼。
hello.js
'use strict'; console.log('Hello, world.'); //另外,若是不寫‘use strict’,能夠在terminal上使用: //node --use_strict hello.js
輸入的js代碼,每行結果自動打印出來。
這相似進入chrome瀏覽器的控制檯,其實至關於啓動了Node解釋器,每輸入一行就執行一行。
而直接使用node <file-name>, 至關於一次性把文件的源代碼給Node解釋器執行了。
在編寫JavaScript代碼的時候,能夠一邊在文本編輯器裏寫代碼,一邊開一個Node交互式命令窗口,
在寫代碼的過程當中,把部分代碼粘到命令行去驗證,效率會提高。
支持es6的語法。如template string。`...``
在node環境下,不支持使用import,export。須要使用module是一個自動生成的對象,用於輸出。
//其基本結構;具體結構進入node,而後輸入global.module。 var module = { id: 'xxx', exports: {} }
module.exportes = variable var var1 = require('相對路徑')
⚠️require()方法,是node.js的modules功能,在chrome browser 控制檯上會報告❌ReferenceError,不支持。
具體見以前的博客:https://www.cnblogs.com/chentianwei/p/10197813.html
由於Node.js是運行在服務區端的JavaScript環境,服務器程序和瀏覽器程序相比,最大的特色是沒有瀏覽器的安全限制了,並且,服務器程序必須能接收網絡請求,讀寫文件,處理二進制內容,因此,Node.js內置的經常使用模塊就是爲了實現基本的服務器功能。這些模塊在瀏覽器環境中是沒法被執行的,由於它們的底層代碼是用C/C++在Node.js運行環境中實現的。
global
進入Node.js交互環境,輸入global.console能夠看到Console對象的屬性
process
也是一個對象,表明當前的進程, 輸入global.process,能夠看到相關信息。
> process === global.process true
相關命令
> process.version; 'v5.2.0' > process.platform; 'darwin' > process.arch; 'x64' > process.cwd(); //返回當前工做目錄 '/Users/michael' > process.chdir('/private/tmp'); // 切換當前工做目錄 undefined > process.cwd(); '/private/tmp'
> process.exit() //退出進程
JavaScript程序是由事件驅動執行的單線程模型,Node.js也不例外。
Node.js不斷執行響應事件的JavaScript函數,直到沒有任何響應事件的函數能夠執行時,Node.js就退出了。
// test.js // process.nextTick()將在下一輪事件循環中調用: process.nextTick(function () { console.log('nextTick callback!'); }); console.log('nextTick was set!');
結果輸出是
nextTick was set!
nextTick callback!
process.nextTick()函數不是立刻執行,而是等到下一次事件循環。
Node.js進程自己的事件就是由process對象來處理的。
若是響應exit事件,就能夠在程序退出時執行某個回調函數:
// 程序即將退出時的回調函數: process.on('exit', function (code) { console.log('about to exit with code: ' + code); });
有些時候,程序自己須要判斷本身究竟是在什麼環境下執行的,經常使用的方式就是根據瀏覽器和Node環境提供的全局變量名稱來判斷:
if (typeof(window) === undefined) { //表明時node.js環境 } else { //瀏覽器環境 }
const fs = require('fs');
用於讀寫文件系統的文件。它提供了異步方法。
var fs = require('fs') fs.readFile('sample.txt', 'utf-8', (err, data) => { if (err) { console.log(err); } else { console.log(data); } })
'sample.txt'是當前路徑內的文件的名字,'utf-8'是文件編碼。
傳入的回調函數接收2個參數err, data,這是標準的Node.js回調函數,第一個參數處理❌,第二個參數處理正確的結果。
例子:
$mkdir htt2 $cd htt2 $npm init -y $touch fs.js $vi sample.txt //編寫一些語句,而後:wq
'use strict'; var fs = require('fs'); fs.readFile('sample.txt', 'utf-8', function (err, data) { if (err) { console.log(err); } else { console.log(data); console.log(data.length + ' bytes'); } });
$node fs
顯示:
hello everybody!
17 bytes
⚠️,若是不使用'utf-u', terminal上顯示一個Buffer對象。
<Buffer 68 65 6c 6c 6f 20 65 76 65 72 79 62 6f 64 79 21 0a>
Buffer對象能夠和String對象作轉換。
//fs.js //在回調函數內加上 var text = data.toString('utf-8') console.log(text)
相反使用: Buffer.from(text, 'utf-8')
//node環境下 > var text = "hello everybody!"> Buffer.from(text, 'utf-8') <Buffer 68 65 6c 6c 6f 20 65 76 65 72 79 62 6f 64 79 21>
fs.readFileSync()
'use strict'; var fs = require('fs'); var data = fs.readFileSync('sample.txt', 'utf-8'); console.log(data);
若是同步讀取文件發生錯誤,則須要用try...catch
捕獲該錯誤
fs.writeFile(fileName, data, callback(err))
一樣,有一個同步方法: writeFileSync(fileName, data)
使用fs.stat(fileName, callback(err, stat))來返回一個Stat對象,它包含文件或目錄的詳細信息。
也有同步函數fs.statSync()
由於Node環境執行的是服務器端代碼,絕大部分須要在服務器上反覆執行邏業務輯的代碼,必須使用異步代碼。否則,同步代碼在執行時期,服務器將中止響應,由於JavaScript只有一個執行線程。
服務器啓動時若是須要讀取配置文件,或者結束時須要寫入到狀態文件時,可使用同步代碼,由於這些代碼只在啓動和結束時執行一次,不影響服務器正常運行時的異步執行。
一個stream是一個抽象接口,用於Node.js內的streaming data。
Stream module提供了基本的API,能夠創建對象執行stream interface.
在Node.js, 流是一個對象。
Node.js提供了許多stream objects,例如,一個發向一個HTTP server的請求,process.stdout。
咱們只須要響應流的事件便可:
例子,讀取流:
var fs = require('fs') //打開一個只讀的流 var rs = fs.createReadStream('sample.txt', 'utf-8') rs.on('data', function(chunk) { console.log(`Data: ${chunk}`) }) rs.on('end', () => { console.log('End.') }) rs.on('error', (err) => { console.log(`"Error: ${err}`) })
Streams是可讀寫的。全部的流都是EventEmitter類的實例。所以可使用它的實例方法了。這些實例方法大可能是用於監聽事件events及相關操做。
好比上例子的on(eventName, listener),當data事件,end事件完成時,同步執行附加的函數。
其實全部的能夠emit事件的objects都是EventEmitter類的實例。這些objects使用on()方法,讓一個或多個函數附加到由這個object發射的event上。
當EventEmitter對象發射emit一個事件時,全部的附加到這個事件的函數被同步地調用。
使用下面的語法,取stream module:
const stream = require('stream');
stream模塊對正在建立新的類型的流實例的開發者來講,是很是有用的。
Developers who are primarily consuming stream objects will rarely need to use the stream
module directly.
那些主要地消耗流對象的開發者則不多須要直接地使用stream module。
4種基本類型:
例子,以流的形式寫入文件,只要不斷調用write()方法,最後end()方法結束。
var ws1 = fs.createWriteStream('sample.txt', 'utf-8') ws1.write('使用Stream寫入文本數據...\n') ws1.write('end!') ws1.end(); var ws2 = fs.createWriteStream('output2.txt'); ws2.write(Buffer.from('使用Stream寫入二進制數據...\n')); ws2.write(Buffer.from('END.')); ws2.end();
全部能夠讀取數據的流都繼承自類stream.Readable
,
全部能夠寫入的流都繼承自類stream.Writable
。
就像能夠把兩個水管串成一個更長的水管同樣,兩個流也能夠串起來。一個Readable
流和一個Writable
流串起來後,全部的數據自動從Readable
流進入Writable
流,這種操做叫pipe
。
Readable.pipe(目的地,選項)方法,就能夠作這件事情。
讓咱們用pipe()
把一個文件流和另外一個文件流串起來,這樣源文件的全部數據就自動寫入到目標文件裏了,因此,這其實是一個複製文件的程序:
var fs = require('fs'); var rs = fs.createReadStream('sample.txt'); var ws = fs.createWriteStream('copied.txt'); rs.pipe(ws);
同時也有事件pipe, unpipe
Readable.pipe的選項end默認是true, 表示end事件觸發後,會自動關閉Writable流。若是不像自動關閉則:
readable.pipe(writable, { end: false });
http模塊會處理Tcp鏈接,解析HTTP。
app不直接和HTTP協議打交道,而是使用http模塊提供的request和response對象
var http = require('http') var server = http.createServer((req, res) => { console.log(`${req.method}:${req.url}`) res.writeHead(200, { 'Content-Type': 'text/html'}) res.end('<h1>hello</h1>') }) server.listen(3000)
request對象應該使用了IncomingMessage類的實例方法method, url。
response對象,是類http.ServerResponse的實例,上面的代碼使用了writeHead方法
crypto模塊的目的是爲了提供通用的加密和哈希算法。
包括:a set of wrappers for OpenSSL's hash, HMAC, cipher, decipher, sign, and verify functions.
用純JavaScript代碼實現這些功能不是不可能,但速度會很是慢。
Nodejs用C/C++實現這些算法後,經過cypto這個模塊暴露爲JavaScript接口,這樣用起來方便,運行速度也快。
MD5是一種hash算法,用於給任意數據一個簽名。這個簽名用一個16進制的string表示。
crypto模塊封裝了hash類。
有2種使用Hash類的方法:
例子1:
//在terminal進入node環境 const crypto = require('crypto') const hash = crypto.createHash('sha256') //產生一個Hash實例 hash.update('some date to hash') //返回 Hash { _options: undefined, writable: true, readable: true, [Symbol(kHandle)]: {}, [Symbol(kState)]: { [Symbol(kFinalized)]: false } } hash.digest('hex') //hex是16進制的意思。 // Prints: // 6a2da20943931e9834fc12cfe5bb47bbd9ae43489a30726962b576f4e3993e50
還可使用更安全的sha256
和sha512
。
例子2
hash對象是一個stream。可讀寫的,當hash被寫入數據後,由於data是能夠被讀的,readable事件會emit.
const crypto = require('crypto'); const fs = require('fs'); const hash = crypto.createHash('sha256'); hash.on('readable', () => { // Only one element is going to be produced by the // hash stream. const data = hash.read(); if (data) { console.log(data.toString('hex')); // Prints: // 6a2da20943931e9834fc12cfe5bb47bbd9ae43489a30726962b576f4e3993e50 } }); hash.write('some data to hash'); hash.end();