Node基礎知識點--學習筆記(一)

龍恩0707發表於2015-04-18

一:建立http伺服器;

在D盤建立一個資料夾node,放入app.js,程式碼如下:

var http = require('http');
http.createServer(function(req,res){
    res.writeHead(200,{'Content-type':'text/html'});
    res.write("<h1>Node.js</h1>");
    res.end("<p>Hello worldA</p>");
}).listen(3000);
console.log("11111");

進入D盤node檔案下,使用命令 node app.js 即可輸出11111,接著在瀏覽器端我們輸入 127.0.0.1:3000 重新整理即可看到瀏覽器下 列印出 Node.js 和 Hello worldA的文案;

上面的程式呼叫了nodeJS提供的http模組,對所有http請求答覆同樣的內容監聽了3000埠,在終端執行這個指令碼發現並沒有退出,而是一直等待,這是因為建立了事件監聽器;我們還發現如果我們修改了app.js程式碼後,需要重新執行下node app.js就可以在瀏覽器端重新整理下就可以看到效果,而不是像php一樣,修改了下直接可以看效果,這是因為nodejs只有在第一次引用到某部分時才會去解析指令碼檔案,以後直接都會去訪問記憶體,避免重複執行,而php不同,因為他總是讀取並執行指令碼,nodejs相比較,效能是提高了,但是對於開發者不方便除錯,因此supervisor可以幫助我們實現這個功能;首先我們需要安裝supervisor;安裝如下:

現在我們可以使用supervisor命令來執行app.js哦!

進入對應目錄後 執行supervisor app.js  也可以執行。之後我們修改程式碼後,直接重新整理下瀏覽器就可以看到效果了!

二:定時器setTimeout(),setInterval(), process.nextTick()及setImmediate()之間的區別;

  1. setTimeout()與setInterval()和瀏覽器的api是一樣的,分別用於單次和多次定時執行任務。呼叫setTimeout()與setInterval()建立的定時器會被插入到定時器觀察者內部的一個紅黑樹中,每次Tick執行時候,會從該紅黑樹中迭代取出定時器物件,檢查是否超過定時時間,如果超過,就形成一個事件,它的回撥函式將會被執行,但是setTimeout()與setInterval()的缺點是:不精確,不準確,比如說setTimeout()或者setInterval()設定一個任務是5毫秒後執行,但是呢在執行4毫秒後,有一個任務也佔了5毫秒的cpu時間片,再次輪到定時器執行時候,時間就已經過期了4毫秒了。
  2. process.nextTick(), 每次執行process.nextTick()時候,它會將回撥函式放入到事件佇列中,也就是有一個回撥函式,在下一次執行Tick時候,將回撥函式取出再執行,執行效率是最高的。
  3. setImmediate(); 此方法和process.nextTick()方法一樣,都是將回撥函式延遲執行;

比如如下程式碼:

process.nextTick(function(){
    console.log('nextTick延遲執行');
});
setImmediate(function(){
    console.log("setImmediate延遲執行");
});
console.log("正常執行");
其執行結果如下:
正常執行
nextTick延遲執行
setImmediate延遲執行

從上面可以看到,process.nextTick()中的回撥函式的執行優先順序高於setImmediate(). 因為事件迴圈對觀察者的檢查是有先後順序的,procee.nextTick()屬於ide觀察者,setImmediate()屬於check觀察者。在每一個迴圈中,ide觀察者優先於check觀察者。

但是在具體實現上,process.nextTick()的回撥函式儲存在一個陣列中,setImmediate()的結果儲存在連結串列中。在行為上,process.nextTick()在每輪迴圈中都會將陣列中的回撥函式全部執行完,而setImmediate()在每輪迴圈中是執行連結串列中的一個回撥函式。如下程式碼:

// 加入2個nextTick()的回撥函式
process.nextTick(function(){
    console.log('nextTick延遲執行1');
});
process.nextTick(function(){
    console.log('nextTick延遲執行2');
});

// 加入2個setImmediate()的回撥函式
setImmediate(function(){
    console.log("setImmediate延遲執行1");
    // 進入下次迴圈
    process.nextTick(function(){
        console.log('強勢插入');
    });
});
setImmediate(function(){
    console.log("setImmediate延遲執行2");
});

console.log("正常執行");
// 其執行結果如下:
nextTick延遲執行1
nextTick延遲執行2
setImmediate延遲執行1
強勢插入
setImmediate延遲執行2

從上面的結果可以看出,當第一個setImmediate()的回撥函式執行完,並沒有立即執行第二個,而是進入了下一輪迴圈,再次按process.nextTick()優先,setImmediate()的順序執行。之所以這樣設計,是為了保證每輪迴圈能夠較快地執行結束,防止cpu佔用過多而阻塞後續的I/O.

 4. 使用require.resolve函式查詢完整模組名;

首先我們在node資料夾目錄下新建app.js, 內容如下:

var str = "aaa";

export.str = str;

定義一個變數,使用export匯出變數,供其他模組呼叫,然後再在相同的目錄下新建一個test.js;內容如下:

console.log(require.resolve('./app.js'));

接著我們進入node資料夾目錄下,執行test.js程式碼,如下命令:

可以看到使用require.resolve方法可以列印出模組的完整的路徑名。

5. require.cache 物件

在node.js中,定義了一個require.cache物件,該物件代表快取了所有已被載入模組的快取區;

我們現在還是以上面的test.js為例:程式碼改成如下:

//console.log(require.resolve('./app.js'));

console.log(require.cache);

接著我們如下執行程式碼可以看到我們的緩衝區如下:

6. __filename變數與__dirname變數。

在node.js中,預定義了2個變數,用於獲取當前模組檔名的__filename變數與用於獲取當前目錄名的__dirname變數;

 1.  __filename變數。

    在任何模組檔案內部,可以使用__filename變數獲取當前模組檔案的帶有完整絕對路徑的檔名。

    還是上面node檔案內,其中在app.js內程式碼改成如下:

    var testMoudle = require('./test.js');

    然後在test.js程式碼內改成如下:

    console.log(__filename);

   執行結果如下:

2. __dirname變數。

   在任何模組檔案內部,可以使用__dirname變數獲取當前模組檔案所在目錄的完整絕對路徑。

   還是上面node檔案內,其中在app.js內程式碼改成如下:

   var testMoudle = require('./test.js');

    然後在test.js程式碼內改成如下:

   console.log(__dirname);

  執行結果如下:

7. 事件處理機制及事件迴圈機制。

EventEmitter類的各種方法;

方法名與引數 含義
addListener(event,listener) 對指定事件繫結事件處理函式
on(event,listener) addListener方法的別名
once(event,listener) 對指定的事件執行一次的處理函式
removeListener(event,listener) 對指定事件解除事件的處理函式
removeAllListeners([event]) 對指定的事件解除所有的事件處理函式
setMaxListeners(n) 指定事件處理函式的最大數量。
listeners(event) 獲取指定事件的所有事件處理函式
emit(event,[arg1],[arg2]) 手工觸發指定事件

下面我們來看看使用on(或者addListener)方法繫結的事件處理函式;

在node資料夾內中的test.js改為下面的程式碼

// 使用on方法繫結事件處理函式
var http = require('http');
var server = http.createServer();
server.on('request',function(req,res){
    console.log(req.url);
    res.end();
});
server.listen(1337);

然後執行node test.js 執行如下:

可以看到控制檯輸出了兩個目標的url地址,其中第一個url地址為使用者輸入的客戶端請求的目標url地址,”/” 代表使用者輸入的目標url地址為web應用程式的根目錄,第二個目標url地址為瀏覽器為頁面在收藏夾中的顯示圖示(預設為favicon.ico)而自動發出請求的目標url地址,但是我們把程式碼寫成如下可以遮蔽收藏夾的請求。

var http = require('http');
var server = http.createServer();
server.on('request',function(req,res){
    if(req.url !== '/favicon.ico') {
        console.log(req.url);
    }
    res.end();
});
server.listen(1337);

如下所示:

我們還可以通過多個on方法的執行對同一個事件繫結多個事件處理函式,程式碼如下:

var http = require('http');
var server = http.createServer();
server.on('request',function(req,res){
    if(req.url !== '/favicon.ico') {
        console.log('接收到客戶端請求。');
    }
});
server.on('request',function(req,res){
    if(req.url !== '/favicon.ico') {
        console.log(req.url);
    }
    res.end();
});
server.on('request',function(req,res){
    if(req.url !== '/favicon.ico') {
        console.log('傳送響應完畢!');
    }
    res.end();
});
server.listen(1337);

執行效果如下:

2. 自定義事件並將其觸發;

// 自定義事件並將其觸發
var http = require('http');
var server = http.createServer();
server.on('customEvent',function(arg1,arg2,arg3){
    console.log('自定義事件被觸發');
    console.log(arg1);
    console.log(arg2);
    console.log(arg3);
});
server.emit('customEvent','自定義引數1','自定義引數2','自定義引數3');
server.listen(1337);

執行如下:

3. 獲取指定事件的事件處理函式的數量.

EventEmitter類自身擁有一個listenerCount方法,可用來獲取某個物件的指定事件的事件處理函式的數量。程式碼如下:

EventEmitter.listenerCount(emitter,event);

在listenerCount方法中,使用2個引數,其中第一個引數用於指定需要獲取那個物件的事件處理函式的數量,第二個引數用於指定需要獲取那個事件的處理函式的數量。

程式碼如下:

var http = require('http');
var events = require('events');
var server = http.createServer();

// 為server伺服器在接收到客戶端請求時觸發的request事件繫結多個事件處理函式
server.on('request',function(req,res){
    if(req.url !== '/favicon.cio') {
        console.log('接收到客戶端請求');
    }
});
server.on('request',function(req,res){
    if(req.url !== '/favicon.cio') {
        console.log(req.url);
    }
    res.end();
});
server.on('request',function(req,res){
    if(req.url !== '/favicon.cio') {
        console.log('傳送響應完畢');
    }
});
server.listen(1337);
console.log(events.EventEmitter.listenerCount(server,'request'));

執行如下:

4. EventEmitter類自身所擁有的事件。

在events模組中,為EventEmitter類本身定義了2個事件,newListener事件與removeListener事件。任何時候,當對繼承了EventEmitter類的子類實列物件繫結事件處理函式時,都將觸發EventEmitter類的newListener事件。如下程式碼:

var http = require('http');
var server = http.createServer();

server.on('removeListener',function(e,f){
    console.log("對"+e+"事件取消事件的處理函式");
    console.log(f);
});
server.on('newListener',function(e,f){
    console.log("對"+e+"事件新增事件的處理函式");
    console.log(f);
});
var testFunction = function(){
    // 為server伺服器在接收到客戶端請求時觸發的request事件繫結多個事件處理函式
    server.on('request',function(req,res){
        if(req.url !== '/favicon.cio') {
            console.log('接收到客戶端請求');
        }
    });
    server.on('request',function(req,res){
        if(req.url !== '/favicon.cio') {
            console.log(req.url);
        }
        res.end();
    });
    server.on('request',function(req,res){
        if(req.url !== '/favicon.cio') {
            console.log('傳送響應完畢');
        }
    });
};
server.on('request',testFunction);
server.removeListener('request',testFunction);
server.listen(1337);

執行如下:

5. 在Node.js中使用偵錯程式。

在node.js中,提供了一個在命令列介面中可以使用的偵錯程式,可以利用該偵錯程式來進行一些應用程式的簡單除錯,列如顯示程式碼,變數及函式的返回值。

一:在命令列視窗中使用偵錯程式;

在命令列視窗中,可以使用node debug 命令來啟用偵錯程式,程式碼如下所示:

node  debug <需要被執行的指令碼檔名>

比如現在在app.js加入如下JS程式碼:

console.log("hello world");
function foo() {
console.log("hello foo");
return 100;
}
var bar = 'this is a pen.';
var http = require('http');
var i = foo();
console.log(i);

截圖如下:

然後在命令列中使用 node debug 命令除錯該指令碼檔案,如下:

如果我們想要繼續執行下面的程式碼,我們可以在debug命令後輸入 “cont”命令或 ”c”命令(“continue”命令的縮寫),以繼續執行剩餘指令碼。如下所示:

但是如果我們不需要執行完剩餘的所有指令碼程式碼,我們可以在 ”debug”命令後輸入”next”命令或”n” 命令,將程式執行到下一句可執行程式碼之前。如下所示:

但是如果我們想要進入函式內部的話,我們可以在上面的debug後面輸入”step”命令或”s”命令,程式將會暫停在函式內第一行程式碼之前。如下圖所示:

比如JS程式碼先把require js去掉 如下程式碼:

console.log("hello world");
function foo() {
console.log("hello foo");
return 100;
}
var bar = 'this is a pen.';
//var http = require('http');
var i = foo();
console.log(i);

執行如下:

當使用”step”命令或”s”命令進入函式內部後,可以繼續使用 ”next”命令或”n” 命令逐句執行函式內部的每一行程式碼,如下圖所示:

在函式內部程式碼被逐句執行的時候,我們可以使用 “out”命令或”o” 命令立即執行完函式內剩餘的所有程式碼,程式將被暫停在呼叫函式的程式碼之後的下一句程式碼之前。如下圖所示:

等等,書上還有很多命令,我這邊就忽略了,我想現在我們可以來學習一下使用更好的一款調式工具---node-inspector ,它通過web網頁與使用者進行互動,比node.js中自帶的調式器更美觀,更方便。

首先我們需要安裝node-inspector,

   1. 在命令列視窗中輸入如下命令

      npm install –g  node-inspector  如下所示:

看到如上資訊,說明已經安裝成功,如果安裝不成功的話,也不要急,重新執行下,因為由於網路的原因引起的,所以會導致安裝不成功,我們可以測試下是否安裝成功,如下命令:

node-inspector 如果出現如下,說明安裝成功了!

在使用node-inspector除錯工具進行除錯時候,首先在命令列視窗中輸入如下所示的命令以除錯指令碼工具。

node  --debug-brk[=port]  filename

比如我現在的demo,我現在要進入我D盤資料夾node內,除錯app.js,那麼我需要進入node資料夾內,輸入如上的命令來開啟除錯工具;如下命令:

node  --debug-brk[=port] app.js

如下:

接著我們需要開啟第二個命令列視窗執行 node-inspector命令以啟動除錯工具,如下所示:

接著我們需要在瀏覽器位址列中輸入http://127.0.0.1:8080/debug?port=5858 網址及埠號(8080用於指定node-inspector的web埠,port=5858用於指定node-inspector的除錯埠,我們也可以在node-inspector安裝目錄下的config.json檔案中修改node-inspector的web埠和除錯埠。)

開啟瀏覽器如下圖所示:

相關文章