一、概覽
1、html可以在瀏覽器直接打開運行,是因爲瀏覽器是html文件的解析器
而js文件不能在瀏覽器直接運行,是因爲瀏覽器不支持解析js文件,需要有一個解析器來解析js文件
可以下載安裝nodejs解析器,並且裏面自帶了npm,通過node.exe可以直接運行js文件,是通過命令行的形式運行的
要運行某個js文件,直接node + 要運行的js文件名稱或者直接是js代碼,按回車即可
js操作的是瀏覽器,node操作的是電腦系統、文件等等
2、編輯器也可以直接運行,推薦使用webstorm
3、在Ecmascript部分,node和js其實是一樣的,比如數據類型的定義、語法結構、內置對象
在js中的頂層對象:window
在node中的頂層對象:global,沒有window這個概念
二、模塊
es6通過import export來進行模塊化
nodejs通過require來進行模塊化
html通過script標籤來引入js文件
1、在nodeJs裏面,一個js文件就是一個模塊,每個模塊都有自己的作用域,
我們使用var聲明的變量,不是全局的,而是屬於當前模塊下的
比如:1.js
var a = 100;
console.log(a); //100
console.log(global.a); //undefined ,不是全局下面的變量
global.a = 200;
console.log(a); //100 仍然是開始定義的100
console.log(global.a); //這時候纔是200
2、filename:表示當前文件的絕對路徑
dirname:表示當前文件所在目錄的絕對路徑
它們並非全局的,而是模塊作用域下的
所以global.filename是訪問不到的,返回undefined
console.log(filename); //E:\webstorm文件夾\index.js
console.log(__dirname); //E:\webstorm文件夾
3、模塊路徑
require(); //可以是絕對路徑、相對路徑
不能直接是require("1.js")這種不帶./的,這種加載方式,會加載node中的核心模塊,或者是node_modules裏面的慕課
4、查找文件:
首先按照加載的模塊的文件名進行查找;
如果沒有找到,則會在模塊文件名稱後面自動加上.js的後綴,然後進行查找;
如果還沒有找到,則會在模塊文件名稱後面自動加上.json的後綴,然後進行查找;
如果還沒有找到,則會在模塊文件名稱後面自動加上.node的後綴,然後進行查找;
如果還沒有找到,就會報錯。
5、
在一個模塊中,通過var定義的變量,其作用域範圍是當前模塊,外部不能夠直接訪問到;
如果我們想一個模塊能夠訪問另一個模塊中定義的變量,可以通過:
- 把變量作爲global對象的一個屬性(不推薦)
global.a = 100; - 使用模塊對象 module,這個對象不是全局的,而是每個模塊下的對象
模塊對象 module用於:保存提供和當前模塊有關的一些信息,下面有很多屬性:
exports:{}; //module的子對象,通過這個對象可以把一個模塊中的局部變量暴露出去,供外部訪問
因爲exports也是一個對象,所以暴露變量出去之前,先聲明一個key值,value值就是這個局部變量var a = 100; module.exports.name = a; require(); //返回值就是module.exports對象
6、在模塊作用域下,還有一個內置的模塊對象,exports,它其實就是module.exports
所以:
module.exports.name = a; 可以寫成 exports.name = a;
【注意】:不要這樣操作
module.exports = [1,2,3]
也不要:exports = [1,2,3],只是追加屬性,不要修改
三、global對象 全局對象
局部:
filename
dirname
全局:
日期對象 var d = new Date()
數組 var arr = new Array(1,2,3)
定時器
三-1、process對象(進程對象)——全局對象
process === global.process //true
可以在任何地方都能訪問到,通過這個對象提供的屬性和方法,使我們可以對當前運行的程序的進程進行訪問和控制
1、process.argv
console.log(process.argv); // [ 'D:\Program Files\nodejs\node.exe', 'E:\webstorm文件夾\2.js' ]
第一個是解析器,第二個是js文件
如果帶上參數,那麼返回的數組裏面就會包含你運行時帶入的參數
舉例: node process.argv a=1 // [ 'D:\Program Files\nodejs\node.exe', 'E:\webstorm文件夾\2.js','a=1' ]
2、process.execPath
開啓當前進程的這個可執行文件的絕對路徑
3、env
返回用戶環境信息
4、version
返回node版本信息
5、versions
返回node以及node依賴包版本信息
6、pid
返回當前進程的pid 進程是node.exe
7、title
返回當前進程的顯示名稱(Getters/Setters)
8、arch
返回當前CPU處理器架構 arm/ia32/x64
9、platform
返回當前操作平臺
10、cwd()
返回當前進程的工作目錄
11、chdir(directory)
改變當前進程的工作目錄
12、memoryUsage()
返回node進程的內存使用情況,單位是byte
12、exit(code)
退出
13、kill(pid)
向進程發送信息
14、IO
a、stdin、stdout:標準輸入輸出流(IO)
標準輸入設備:向計算機輸入數據和信息的設備,是計算機與用戶或其他設備通信的橋樑。
例如:鼠標、鍵盤、攝像頭等等
標準輸出設備:是計算機硬件系統的終端設備,把計算機數據或信息以數字、字符、圖像、聲音等形式表現出來。例如:顯示器、打印等等
stdin、stdout提供了操作輸入數據和輸出數據的方法,我們也通常稱爲IO操作
stdin:標準輸入流
stdout:標準輸出流
console.log(...) === process.stdout.write(...);
b、默認情況下,輸入流是關閉的,要監聽處理輸入流數據,首先要開啓輸入流
//開啓
process.stdin.resume();
監聽用戶的輸入數據,輸入完成按下回車
process.stdin.on('data',function(chunk){
console.log('用戶輸入了:'+chunk);
})
會一直監聽,用戶輸入完成按下回車顯示數據,再按下仍然會顯示第二次輸入的數據
四、Buffer類:類似數組
1、定義:一個用於更好的操作二進制數據的類
我們在操作文件或者網絡數據的時候,其實操作的就是二進制數據流;Node爲我們提供了一個更加方便的去操作這種數據流的類Buffer,他是一個全局的類,global.Buffer === buffer
JavaScript 語言自身只有字符串數據類型,沒有二進制數據類型。
但在處理像TCP流或文件流時,必須使用到二進制數據。因此在 Node.js中,定義了一個 Buffer 類,該類用來創建一個專門存放二進制數據的緩存區。
在 Node.js 中,Buffer 類是隨 Node 內核一起發佈的核心庫。Buffer 庫爲 Node.js 帶來了一種存儲原始數據的方法,可以讓 Node.js 處理二進制數據,每當需要在 Node.js 中處理I/O操作中移動的數據時,就有可能使用 Buffer 庫。原始數據存儲在 Buffer 類的實例中。一個 Buffer 類似於一個整數數組,但它對應於 V8 堆內存之外的一塊原始內存。
- Buffer.alloc(size); 創建一個Buffer對象,並且爲這個對象分配一個空間大小,大小是size的8位字節;當我們爲一個buffer對象分配了一個空間大小以後,其長度是固定的,不能更改。也就是後續不能再增加內容了
<Buffer 72 75 6e 6f 6f 62> //控制檯顯示的是16進制的 -
Buffer.from(array): 返回一個被 array 的值初始化的新的 Buffer 實例(傳入的 array 的元素只能是數字,不然就會自動被 0 覆蓋),聲明並賦值
- Buffer.from(string[, encoding]): 返回一個被 string 的值初始化的新的 Buffer 實例,encoding默認是utf8
let bf2 = Buffer.from('miaov'); //bf2.length 5 返回的是字節數,不是字符串長度
let bf3 = Buffer.from('妙味'); //bf3.length 6 一個漢字佔據3個字節
2、Buffer類方法
- bf.length; //buffer的字節長度
- bf[ index ]; //獲取或者設置在index索引位置的8位字節內容
- bf.write(string , [offset] , [length] , [encoding]); //根據參數offset偏移量和指定的encoding編碼方式,將參數string寫入數據寫入buffer
- bf.toString( [encoding],[start],[end] ); //根據encoding返回一個解碼的string類型
- bf.toJSON(); //返回一個JSON表示的Buffer實例,JSON.stringify將會默認調用來字符串序列化這個Buffer實例
- bf.slice( [start],[end] ); //返回一個新的buffer,這個buffer將會和老的buffer引用相同的內存地址;
注意:修改這個新的buffer實例slice切片,也會改變原來的buffer - bf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]])
let bf2 = Buffer.from('miaov'); console.log(bf2); //<Buffer 6d 69 61 6f 76> //十六進制 console.log(bf2.toString()); //'miaov' for (let i=0;i<bf2.length;i++){ //m的十六進制是6d;十進制是109;二進制是0110 1101 console.log(bf2[i]); //109 105 97 111 118 十進制 console.log(String.fromCharCode(bf2[i])) // m i a o v 圖形字符 } let bf = Buffer.alloc(5); let str = 'miaov'; //bf.write(str); //console.log(bf); //<Buffer 6d 69 61 6f 76> // bf.write(str,1); // console.log(bf); //從buffer第1位開始寫入,第0位用00補上,<Buffer 00 6d 69 61 6f> bf.write(str,1,3); console.log(bf); //只寫入三位,其他位用00補上,<Buffer 00 6d 69 61 00> console.log(bf.toJSON()); //{ type: 'Buffer', data: [ 0, 109, 105, 97, 0 ] }
3、Buffer中的靜態方法(又叫類方法,即不需要實例化即可使用的方法)
- Buffer.isEncoding(encoding):如果給定的編碼encoding是有效的,返回true;否則返回false
- Buffer.isBuffer(obj); 測試這個obj是否是一個Buffer
- Buffer.byteLength(string,[encoding]):將會返回這個字符串的真實byte字節長度。encoding默認編碼是utf8
let str1 = 'json'; console.log(str1.length); //4 字符長度 console.log(Buffer.byteLength(str1)); //4 字節長度
- Buffer.concat(list,[totalLength]):返回一個保存着將傳入Buffer數組中所有buffer對象拼接在一起的buffer對象
list是buffer對象,返回值是多個buffer對象拼接成的一個buffer對象
totalLength是拼接後的字節長度,不傳是計算後的所有的字節長度,(建議預先設置,節省計算時間)
五、文件系統模塊 File System
1、該模塊是核心模塊,需要使用require導入後使用:let fs = require('fs');
2、該模塊提供了操作文件的一些API
-
異步的打開一個文件:
fs.open(path,flags,[mode],callback)
path:要打開文件的路徑;
flags:打開文件的方式:讀/寫
mode:設置文件的模式:讀/寫/執行
callback:回調函數,接受2個參數:err、fd
err:文件打開失敗的錯誤保存在err裏面,如果成功err爲null
fd:被打開文件的標識,和定時器類似,用於後續要操作文件的標識,即後續如果要操作某個打開的文件,把這個文件標識傳到後續操作的某些方法裏面去,即可知道操作的是當前文件 -
fs.open()的同步版:
fs.openSync(path,flags,[mode]) -
從指定的文檔標識符fd讀取文件數據
fs.read(fd,buffer,offset,length,position)
fd:通過open方法成功打開一個文件返回的編號
buffer:buffer對象,用於存儲打開文件內部的信息
offset:偏移量,數據從buffer對象裏面的第幾位開始存儲,起始是0
length: 讀取的內容長度
position:從文件的第幾位開始讀取,默認是0,即從頭開始
callback:接受三個參數,err錯誤信息、length返回的新的buffer對象的長度;newBf:返回的新的buffer對象 - fs.read的同步版本,返回bytesRead的個數
fs.readSync(fd,buffer,offset,length,position,callback)
*通過文件標識fd,向指定的文件中寫入buffer
fs.write(fd,buffer,offset,length,[position],callback)
同步版本:fs.writeSync(fd,buffer,offset,length,[position])
fd:通過open方法成功打開一個文件返回的編號
buffer:要寫入到文件的數據
offset:從buffer對象中第幾位開始寫入數據,默認是0
length: 要寫入的buffer數據的長度
position:向指定的文件的第幾位開始寫入buffer數據,默認是0,即從頭開始寫入數據,如果開頭已經有數據,那麼新的數據會覆蓋原始數據
callback:接受三個參數,err錯誤信息、length寫入數據的buffer對象的長度;newBf:寫入數據的buffer對象
*通過文件標識fd,把data寫入到文檔中,如果data不是buffer對象的實例,則會把值強制轉換成爲一個字符串
fs.write(fd,data,[position],[encoding],callback)
同步版本:fs.writeSync(fd,data,[position],[encoding])
data:直接是一個字符串,此時不再從第幾位寫入了,而是全部寫入到文件裏,所以沒有offset參數
- 關閉一個打開的文件
fs.close(fd,callback)
同步版本:fs.closeSync(fd)
六、更好用的文件系統方法
1、fs.writeFile(filename,data,[options],callback)
異步的將數據寫入一個文件,如果文件不存在就新建,如果文件原先存在,則會被替換;data可以是一個string,也可以是一個原生buffer
同步版本:fs.writeFileSync(filename,data,[options])
2、fs.appendFile(filename,data,[options],callback)
異步的將數據添加到一個文件的尾部,如果文件不存在就新建,如果文件原先存在,則會將數據追加到文件的尾部;data可以是一個string,也可以是一個原生buffer
同步版本:fs.appendFileSync(filename,data,[options])
3、fs.readFile(filename,[options],callabck)
異步讀取一個文件的全部內容
同步版本:fs.readFileSync(filename,[options])
4、fs.exists(path,callback)
檢查指定路徑的文件或者目錄是否存在
同步版本:fs.existsSync(path)
5、fs.unlink(path,callback)
刪除一個文件
同步版本:fs.unlink(path)
let fs = require('fs');
let filename = '2.txt';
//異步
if(fs.exists(filename,(err)=>{
if(!err){
fs.writeFile(filename,'hello',(err)=>{
if(err){
console.log("文件創建失敗")
}else{
console.log("文件創建成功")
}
})
}else{
fs.appendFile(filename,'-world',(err)=>{
if(err){
console.log('文件追加失敗')
}else{
console.log('文件追加成功')
}
})
}
}));
//同步
if(!fs.existsSync(filename)){
console.log('文件創建成功');
fs.writeFileSync(filename,'hello')
}else{
console.log('文件追加成功');
fs.appendFileSync(filename,'-world')
}
6、fs.rename(oldPath,newPath,callback)
重命名
同步版本:fs.renameSync(oldPath,newPath)
7、fs.stat(path,callback)
讀取文件信息的狀態,不是文件內部的內容,返回值是一個json,包括文件類型、文件日期、文件大小等等
mode=33206表示該文件是文件類型,mode=16822表示該文件是文件夾
同步版本:fs.statSync(path)
8、fs.watch(filename,[options],[listener])
觀察指定路徑的改變,filename可以是文件或者目錄
//listener是一個監聽器,回調函數,有2個參數:事件event和被監聽文件的名稱filename
9、fs.mkdir(path,[mode],callback) //mode模式,是隻讀還是讀寫模式
創建文件夾
同步版本:fs.mkdirSync(path,[mode])
10、fs.readdir(path,callback)
讀取文件夾
同步版本:fs.readdirSync(path)
回調函數包括2個參數,err錯誤信息和當前文件夾下面所有的文件和文件夾信息fileList
11、fs.rmdir(path,callback)
刪除文件夾
同步版本:fs.rmdirSync(path)
七、使用node進行web開發
1、用戶上網流程
用戶輸入https://www.baidu.com/
通過瀏覽器發送https請求(類似於打電話撥號),請求baidu.com,這個域名可以轉換爲ip地址
通過ip定位,到網絡中指定的一臺機器上,然後該機器會接收到該用戶發送過來的請求,然後對該請求進行處理,返回對應的內容
簡言之:
用戶通過瀏覽器發送一個http請求到指定的服務器
服務器接收到該請求,對該請求進行分析和處理
服務器處理完成之後,返回對應的數據到用戶機器
瀏覽器接受到服務器返回的數據,並根據接收到的數據進行分析和處理
web開發:就是兩臺機器之間的數據交互,客戶端與服務端的交互
由客戶端發送一個http請求到指定的服務端 ——> 服務端接收並處理請求 ——> 返回數據到客戶端,類比迅雷下載
http協議:
要進行web開發,首先就必須處理關於服務端接收請求的這一過程,所以要搭建服務器,該服務器可以接收來自任何客戶端的鏈接和請求,然後進行對應處理
2、http模塊
let http = require('http');
使用該模塊可以進行服務器的搭建
server.listen(); //如果不傳參數,默認系統會自動分配端口號,但是不建議,要主動設置端口號及主機名
res.writeHead( 200,'',{ //200,默認返回ok
content-type : 'text/html;charset=utf-8' //會將返回信息當成html輸出到頁面,html標籤起作用
content-type : 'text/plain' //會將返回信息當成文本文件輸出到頁面,html標籤不起作用
} )
/*
* 搭建一個http的服務器,用於處理用戶發送的http請求
* 需要使用node提供一個模塊 http
* */
//加載一個http模塊
let http = require('http');
//通過http模塊下的createServer創建並返回一個web服務器對象
let server = http.createServer();
server.on('error',(err)=>{
console.log(err)
})
server.on('listening',()=>{
console.log('linstening...')
})
server.on('request',(req,res)=>{ //req是客戶端發送的數據,res是服務端返回的數據
console.log('有客戶端請求了')
//console.log(req); //req裏面有請求頭、域名後面附帶的路徑信息/a/index.html、method方式
//console.log(res); //res裏面包括response正文信息(即網頁上顯示的內容)以及正文之外的一些信息,比如返回頭信息(網頁上不可見)
res.writeHead('200','',{
'content-type':'text/html;charset=utf-8'
})
// res.write('<h1>hello</h1>');
// res.end();
//可以合併爲一步
res.end('<h1>hello</h1>');
})
server.listen(8080,'localhost');
3、url模塊
let url = require('url');
url.parse(); //專門用於解析url路徑,返回json格式數據
4、使用querystring模塊方法對get和post提交的數據進行處理
let qs = require('querystring');
get方式發送數據
qs.parse('foo=bar&abc=xyz&abc=123'); //{foo: 'bar',abc: ['xyz', '123']}
post方式發送數據
let http = require('http');
let url = require('url');
let fs = require('fs');
let qs = require('querystring');
let server = http.createServer();
let filename = __dirname + '/html/';
server.on('error',(err)=>{
console.log(err)
})
server.on('listening',()=>{
console.log('listening')
})
server.on('request',(req,res)=>{
var urlStr = url.parse( req.url ); //返回一個對象,裏面的pathname屬性和req.url一樣
switch (urlStr.pathname) {
case '/':
getData(filename+'index.html',req,res);
break;
case '/user':
getData(filename+'user.html',req,res);
break;
case '/login':
getData(filename+'login.html',req,res);
break;
case '/login/check':
/*console.log(req.method); //get
console.log(urlStr.query); //username=xiaoxiao&password=1
console.log(qs.parse(urlStr.query)); //[Object: null prototype] { username: 'xiaoxiao', password: '1' }*/
if(req.method.toUpperCase() === 'POST'){
let str = '';
req.on('data',(chunk)=>{
str += chunk;
})
req.on('end',()=>{
console.log(str); //username=13795232495&password=111
console.log(qs.parse(str)); //[Object: null prototype] { username: '13795232495', password: '111' }
})
}
break;
default:
break;
}
})
function getData(filename,req,res) {
fs.readFile(filename,(err,data)=>{
//console.log(data.toString()); //輸出頁面內的全部標籤內容
if(err){
res.writeHead(404,'',{
'content-type':'text/html;charset=utf8'
})
res.end('<h1>404</h1>')
}else{
res.writeHead(200,'',{
'content-type':'text/html;charset=utf8'
})
res.end(data)
}
})
}
server.listen(8082,'localhost');
/*
* 搭建一個http的服務器,用於處理用戶發送的http請求
* 需要使用node提供一個模塊 http
* */
//加載一個http模塊
let http = require('http');
let url = require('url');
//通過http模塊下的createServer創建並返回一個web服務器對象
let server = http.createServer();
server.on('error',(err)=>{
console.log(err)
})
server.on('listening',()=>{
console.log('linstening...')
})
server.on('request',(req,res)=>{ //req是客戶端發送的數據,res是服務端返回的數據
console.log('有客戶端請求了')
console.log(req.url); //req裏面有請求頭、域名後面附帶的路徑信息/a/index.html、method方式
//console.log(res); //res裏面包括response正文信息(即網頁上顯示的內容)以及正文之外的一些信息,比如返回頭信息(網頁上不可見)
switch (req.url) {
case '/':
res.writeHead(200,'',{
'content-type':'text/html;charset=utf-8'
})
res.end('<h1>這是首頁</h1>');
break;
case '/user':
res.writeHead(200,'',{
'content-type':'text/html;charset=utf-8'
})
res.end('<h1>這是用戶頁</h1>');
break;
default:
res.writeHead(404,'',{
'content-type':'text/html;charset=utf-8'
})
res.end('<h1>404</h1>');
break;
}
// res.write('<h1>hello</h1>');
// res.end();
//可以合併爲一步
//res.end('<h1>hello</h1>')
})
server.listen(8080,'localhost');
【注意】:
1、所謂異步,就是執行成功了之後會執行一個回調函數,下面的代碼會繼續執行,即使報錯了也不妨礙下面代碼的執行;同步,就是成功了就繼續執行下面的代碼,報錯了就卡在這兒了,下面的代碼也就執行不了了。
2、所謂的參數filename,包括文件的名稱和路徑(如果有路徑的話),比如在task文件夾下創建index.html
那麼filename='task/index.html'
要包括文件的名稱,不能僅僅是一個空路徑,'task/'