Node.js是什么?它和JavaScript是什么關(guān)系?
不點(diǎn)藍(lán)字關(guān)注,我們哪來故事?
JavaScript 誕生于 1995 年,幾乎是和互聯(lián)網(wǎng)同時出現(xiàn);Node.js 誕生于 2009 年,比 JavaScript 晚了 15 年左右。
在 Node.js 之前,JavaScript 只能運(yùn)行在瀏覽器中,作為網(wǎng)頁腳本使用,為網(wǎng)頁添加一些特效,或者和服務(wù)器進(jìn)行通信。有了 Node.js 以后,JavaScript 就可以脫離瀏覽器,像其它編程語言一樣直接在計算機(jī)上使用,想干什么就干什么,再也不受瀏覽器的限制了。
Node.js 不是一門新的編程語言,也不是一個 JavaScript 框架,它是一套 JavaScript 運(yùn)行環(huán)境,用來支持 JavaScript 代碼的執(zhí)行。用編程術(shù)語來講,Node.js 是一個 JavaScript 運(yùn)行時(Runtime)。

圖1:Node.js 圖標(biāo)
現(xiàn)在的 JavaScript 除了用于 Web 前端編程(網(wǎng)頁編程),還能干很多事情,比如:
開發(fā)網(wǎng)站后臺,這原本是 PHP、Java、Python、Ruby 等編程語言擅長的;
開發(fā) GUI 程序,也就是我們常說的帶界面的電腦軟件,比如 QQ、360、迅雷等;
手機(jī) APP,包括 Android APP、iOS APP;
CLI 工具,也就是不帶界面的命令行程序。
你看,學(xué)會了 JavaScript,你不但是全棧工程師,甚至還將無所不能,這都是 Node.js 帶給我們的。
Node.js 官網(wǎng)中文版:https://nodejs.org/zh-cn/
運(yùn)行時是什么?
對于 JavaScript 來說,它在運(yùn)行期間需要依賴以下組件:
1) 解釋器
JavaScript 是一種腳本語言,需要一邊解釋一邊運(yùn)行,用到哪些源代碼就編譯哪些源代碼,整個過程由解釋器完成。沒有解釋器的話,JavaScript 只是一堆純文本文件,不能被計算機(jī)識別。
2) 標(biāo)準(zhǔn)庫
我們在 JavaScript 代碼中會調(diào)用一些內(nèi)置函數(shù),這些函數(shù)不是我們自己編寫的,而是標(biāo)準(zhǔn)庫自帶的。
3) 本地模塊
所謂本地模塊,就是已經(jīng)被提前編譯好的模塊,它們是二進(jìn)制文件,和可執(zhí)行文件在內(nèi)部結(jié)構(gòu)上沒有什么區(qū)別,只是不能單獨(dú)運(yùn)行而已。這些本地模塊其實(shí)就是動態(tài)鏈接庫(在 Windows 下是 .dll 文件),如果你使用過C語言、C++ 等編譯型語言,那你應(yīng)該能夠更好地理解它。
JavaScript 的很多功能都需要本地模塊的支持,比如:
Cookie 用于存儲少量的用戶數(shù)據(jù),它是用戶計算機(jī)上的一種小文件,使用 Cookie 必須有文件操作模塊的支持。
Ajax 可以借助互聯(lián)網(wǎng)從服務(wù)器請求數(shù)據(jù),這是一種網(wǎng)絡(luò)操作,必須有網(wǎng)絡(luò)庫的支持。
一步一步跟蹤代碼的執(zhí)行流程,從中發(fā)現(xiàn)邏輯錯誤,這個過程叫做調(diào)試,需要有調(diào)試器(Debugger)的支持。
JavaScript 可以操作 HTML,這需要 HTML 解析模塊提前構(gòu)建起 DOM 樹。
本地模塊一般封裝了通用功能,對性能要求較高,所以通常使用編譯型語言來實(shí)現(xiàn),比如C語言、C++、匯編語言等。
JavaScript 解釋器需要本地模塊的支持,標(biāo)準(zhǔn)庫在編寫時也會調(diào)用本地模塊的接口,而我們編寫的 JavaScript 代碼一般不會直接使用本地模塊,所以 Web 前端程序員觸及不到它們。
本地模塊是幕后英雄,它不顯山露水,但是又不可或缺。
總結(jié)
在 Node.js 之前,JavaScript 運(yùn)行時被綁定在瀏覽器中,作為瀏覽器的各種模塊出現(xiàn)。這意味著,要想運(yùn)行 JavaScript 代碼就必須啟動瀏覽器,JavaScript 逃不出瀏覽器的手掌心,它的功能受到很大的限制,只能作為網(wǎng)頁腳本使用。
Node.js 的誕生
谷歌公司在 Chrome 瀏覽器中集成了一種名為“V8”的 JavaScript 引擎(也即 JavaScript 解釋器),它能夠非??焖俚亟馕龊蛨?zhí)行 JavaScript 代碼。
V8 引擎使用 C++ 語言編寫,可以獨(dú)立運(yùn)行,也可以嵌入到任何其它 C++ 程序中。谷歌公司將 V8 引擎甚至整個 Chrome 瀏覽器都開源了,任何人都可以免費(fèi)地將 V8 應(yīng)用到自己的項目中。
V8 引擎的強(qiáng)大,以及當(dāng)年 JavaScript 的火爆,使得一名叫 Ryan Dahl 的程序員動起了“歪心思”,他希望在瀏覽器之外再為 JavaScript 構(gòu)建一個運(yùn)行時,讓 JavaScript 能夠直接在計算機(jī)上運(yùn)行,這樣 JavaScript 就能像 Python、Ruby、PHP 等其它腳本語言一樣大展宏圖,不必再受限于瀏覽器,只能做一些小事情。
Ryan Dahl 和他的團(tuán)隊真的做到了,并且做得很好,他們將這套獨(dú)立的 JavaScript 運(yùn)行時命名為 Node.js;為了避免被誤認(rèn)為是 JavaScript 框架,現(xiàn)在也經(jīng)常簡稱為 Node。

圖2:V8 引擎圖標(biāo)
V8 引擎官網(wǎng):https://v8.dev/
Node.js 的組成
1) V8 引擎
V8 引擎就是 JavaScript 解釋器,它負(fù)責(zé)解析和執(zhí)行 JavaScript 代碼。
V8 引擎借鑒了 Java 虛擬機(jī)和 C++ 編譯器的眾多技術(shù),它將 JavaScript 代碼直接編譯成原生機(jī)器碼,并且使用了緩存機(jī)制來提高性能,這使得 JavaScript 的運(yùn)行速度可以媲美二進(jìn)制程序。
2) 本地模塊
Node.js 集成了眾多高性能的開源庫,它們使用 C/C++ 語言實(shí)現(xiàn),比如:
| 模塊 | 說明 |
|---|---|
| libuv | 一個跨平臺的、基于事件驅(qū)動的異步 I/O 庫。但是 libuv 不僅限于 I/O,它還提供了進(jìn)程管理、線程池、信號處理、定時器等其它功能。 Linux 中一切皆文件,這里的 I/O 不僅僅包括文件讀寫,還包括數(shù)據(jù)庫讀寫、網(wǎng)絡(luò)通信(socket)等。 |
| nmp | Node.js 包管理器,可以下載包、安裝包、卸載包、更新包、上傳包等。 |
| http_parser | 一款由C語言編寫的輕量級 HTTP 解析器,用以支持 Web 應(yīng)用開發(fā)。 |
| zlib | 工業(yè)級的數(shù)據(jù)壓縮/解壓模塊,Nodejs 借助 zlib 來創(chuàng)建同步、異步或者流式的壓縮/解壓接口。 |
| OpenSSL | 該模塊提供了經(jīng)過嚴(yán)密測試的許多加密/解密功能,現(xiàn)代 Web 依賴這些功能來實(shí)現(xiàn)安全性,比如 SSL 協(xié)議和 https 協(xié)議。 |
| c-ares | 異步 DNS 查詢和解析庫。 |
Node.js 直接在計算機(jī)上運(yùn)行 JavaScript 代碼,并且要賦予 JavaScript 強(qiáng)大的能力,所以它的本地模塊和瀏覽器中的運(yùn)行時有很多大區(qū)別,甚至說幾乎沒有什么關(guān)聯(lián)。Node.js 幾乎完全拋棄了瀏覽器,自己從頭構(gòu)建了一套全新的 JavaScript 運(yùn)行時。
3) 標(biāo)準(zhǔn)庫
本地模塊使用 C/C++ 編寫,而 Node.js 面向 JavaScript 開發(fā)人員,所以必須要封裝本地模塊的 C/C++ 接口,提供一套優(yōu)雅的 JavaScript 接口給開發(fā)人員,并且要保持接口在不同平臺(操作系統(tǒng))上的一致性。
這套 JavaScript 接口,就是 Node.js 標(biāo)準(zhǔn)庫。標(biāo)準(zhǔn)庫是否優(yōu)雅和強(qiáng)大,決定了 Node.js 的易用性,直接影響 Node.js 的市場表現(xiàn)。
總結(jié)
V8 引擎和眾多本地模塊都是現(xiàn)成的,別人已經(jīng)造好了輪子,Node.js 的主要工作就是選擇合適的模塊,將它們集成在一起,并編寫好 JavaScript 接口。
當(dāng)然,并不是所有的本地模塊都能找到合適的,Node.js 也自己編寫了幾個模塊,典型的代表就是 Libuv。Libuv 是 Node.js 最核心最基礎(chǔ)的模塊,Node.js 完全基于 Libuv 而構(gòu)建。
你可能聽說過 Node.js 采用了基于事件的、單線程的異步 I/O 架構(gòu),這是 Node.js 最大的特點(diǎn),也是它和其它腳本語言最大的區(qū)別,Node.js 的這種能力就是依賴 Libuv 實(shí)現(xiàn)的。
Libuv 如此強(qiáng)大,官方?jīng)Q定將它從 Node.js 中剝離出來,作為一個單獨(dú)的網(wǎng)絡(luò)庫發(fā)布,并且開源免費(fèi)?,F(xiàn)在的 Libuv 已經(jīng)變得非常流行,和傳統(tǒng)的 Libevent 和 libev 庫并稱為“C/C++ 三大網(wǎng)絡(luò)庫”。
Node.js 之所以大名鼎鼎,主要是因?yàn)樗捎昧?V8 引擎和 Libuv 庫:V8 引擎保證了 Node.js 運(yùn)行高效,Libuv 庫提供了基于事件循環(huán)的異步 I/O 能力。

圖3:Libuv 圖標(biāo)
Libuv 官網(wǎng):http://libuv.org/
總結(jié)
最后我們來匯總一下 JavaScript 和 Node.js 的歷史:
Netscape 瀏覽器衍生出了 JavaScript 腳本,賦予網(wǎng)頁編程能力;
Chrome 瀏覽器衍生了 V8 引擎,提高了 JavaScript 性能;
V8 引擎構(gòu)建了 Node.js,拓展了 JavaScript 的編程能力;
Node.js 衍生了 Libuv 庫,給網(wǎng)絡(luò)開發(fā)增加了一款優(yōu)秀的工具。
本文于?2021-09-22 發(fā)布于C語言中文網(wǎng)
END
若覺得文章對你有幫助,隨手轉(zhuǎn)發(fā)分享,也是我們繼續(xù)更新的動力。
長按二維碼,掃掃關(guān)注哦
?「C語言中文網(wǎng)」官方公眾號,關(guān)注手機(jī)閱讀教程??

