1. 程式人生 > 實用技巧 >JS高階--node.JS

JS高階--node.JS

大家需要知道nodejs 的出現,允許我們js開發人員使用 js 來開發伺服器。

node.js 特點
  • 它是一個 javascript 的執行環境

  • 依賴於 V8 引擎進行程式碼解析

  • 事件驅動

  • 非阻塞 I/O

  • 輕量、可伸縮

  • 單執行緒:指的是 nodejs 在執行 js 程式碼的時候是單執行緒,並不是說整個 node.js 是單執行緒。

REPL 環境,英語全稱 read-eval-print-loop,翻譯成中文就稱之為“即時執行環境”,在這個環境裡面,可以執行 js 程式碼。

該環境的意義在於測試一些小段的程式碼。一般比較的成熟的語言都提供REPL環境。

模組化

如果想要開發大型應用,必須要有模組化。

最早,js 是沒有模組話的概念的,因為最早 js 的定位就是指令碼語言(玩具語言)。

但是隨著 js 走向伺服器端的開發,那麼必然也是需要模組化。

總結一下模組化的好處:

  • 生產效率高

  • 維護成本低

接下來我們就需要看一下 node.js 中如何實現模組化,一般來講,分為3類

  • 檔案模組

  • 核心模組

  • 第三方模組

引入模組統一使用 require 方法。

const readline = require('readline-sync');  
檔案模組
require(“路徑.副檔名”)

// 相對路徑
require('./index.js');
require('../index.js');
// 絕對路徑
require('/src/index.js'); // 一個斜槓 / 代表根目錄  
核心模組

核心模組是指 nodejs 內建的模組,比如 fs、os、http、path...

require('fs');
require('path');
第三方模組

所謂第三方模組,就是指非官方開發,而是第三方開發的。

require('readline-sync'); 
CommonJS 規範

之前,我們接觸過一個東西,叫做 ECMAScript,它就是 js 語言的規範。

我們可以這麼說,ECMAScript 是規範,JavaScript 是這個規範的一種實現,例如 adobe 的 flash 裡面使用的 ActionScript 也是一種 ECMAScript 的實現。

ECMAScript 雖然說是 js 的規範,但是它只管客戶端,Commonjs 管的就是瀏覽器端以外的環境。

CommonJS一些著名的實現,node.js、mongodb、couchdb、coffiejs。

CommonJS 中的模組匯入匯出

在 CommonJS 中,通過 module.exports 來匯出一個模組,通過 require 來匯入一個模組。

module.exports 可以將他想象成一個物件。

// index.js
// 匯出模組
module.exports.name = 'xiejie';
module.exports.sayHello = function(){
    console.log('Hello');
}

// index2.js
// 匯入模組
let obj = require('./index.js');
console.log(obj.name); // xiejie
obj.sayHello(); // Hello

  為了讓開發人員使用起來更加方便,nodejs 還提供了一個 exports 的物件,它是指向 module.exports 的。

// index.js
// 匯出模組
exports.name = 'xiejie';
exports.sayHello = function(){
    console.log('Hello');
}

// index2.js
// 匯入模組
let obj = require('./index.js');
console.log(obj.name); // xiejie
obj.sayHello(); // Hello

  但是兩者之間是有區別,上面我們在介紹 exports 的時候,我們有講過, exports 是指向 module.exports 的,也就是說,最終,匯出的是 module.exports

// index.js
module.exports = 'F71';
// index2.js
let obj = require('./index.js');
console.log(obj); // F71


// index.js
exports = 'F71';
// index2.js
let obj = require('./index.js');
console.log(obj); // {}

  

nodejs 的兩大特點

nodejs 有兩個最大的特點:非同步無阻塞事件驅動

1. 非同步無阻塞

傳統的同步程式碼,如果在它的執行緒中遇到磁碟讀寫、傳送請求,就會阻塞後面的程式碼。像 Java 這種語言,採用的是多執行緒的方式來解決這個問題。

js 採用的非同步無阻塞的方式來解決這個問題,遇到非同步程式碼,會交給非同步模組。

下面是同步處理阻塞IO的示例:

const fs = require('fs');
console.log('開始寫入檔案');
// 如果要捕獲錯誤,使用 try...catch
try {
    // 書寫嘗試執行的程式碼
    const content = fs.readFileSync('./test111.txt');
    console.log(content.toString());
} catch (err) {
    console.log(err);
}
console.log('結束寫入檔案');

  非同步的方式:

const fs = require('fs');
console.log('開始寫入檔案');
fs.readFile('./test.txt',function(err,data){
    if(err) throw err;
    console.log(data.toString());
})
console.log('結束寫入檔案');

  

通過上面的例子,我們可以看出,在 node中,由於採用非同步的方式來處理阻塞程式碼,所以在回到函式中,採用錯誤優先原則。

  1. 事件驅動

這裡其實就是兩個知識點:EventEmitter,事件驅動模式,EventEmitter 是 node 裡面為我們提供的一個模組,允許我們自定義事件。

之前我們學過事件,例如 click,mouseenter,mousemove 這些。

例如:

const EventEmitter = require('events').EventEmitter;

const event = new EventEmitter();

// 手動定義一個事件
event.on('F71',function(){
    console.log('你觸發了這個事件');
})

// 手動來觸發
setTimeout(function(){
    event.emit('F71');
},2000);
nodejs 系統架構

nodejs 的架構如下圖:

該圖展示了整個 Node 的執行原理,從左到右,從上到下,整個 Node 被分為了 4 層,分別是應用層V8 引擎層Node API層LIBUV層

應用層:即 JavaScript 互動層,常見的就是 Node 的模組,比如httpfs

V8 引擎層:即利用 V8 引擎來解析 JavaScript 語法,進而和下層 API 互動。

NodeAPI 層:為上層模組提供系統呼叫,一般是由 C 語言來實現,和作業系統進行互動。

LIBUV 層:是跨平臺的底層封裝,實現了事件迴圈、檔案操作等,是 Node 實現非同步的核心。

無論是 Linux 平臺還是 Windows 平臺,Node 內部都是通過執行緒池來完成非同步 I/O 操作的,而 LIBUV 針對不同平臺的差異性實現了統一呼叫。

當我們使用 Node.js 的時候,會在 JavaScript 中觸發一些命令呼叫方法,這些方法會被包裝成一個物件,放入執行緒池,然後前面的方法就返回了,繼續執行下面的 JavaScript 程式碼。

執行緒池中採用多執行緒的方式執行,執行完的物件放入事件迴圈佇列。

事件迴圈佇列採用類似 while(true) 這種迴圈的方式,不斷的檢視是否有事件,並且讀取是否包含回撥,由於前面回撥函式被包裝到物件中,這裡直接呼叫執行就可以了。

因此,Node.js 的單執行緒僅僅是指 JavaScript 執行在單執行緒中,而並非整個 Node.js 執行環境是單執行緒。