標籤: node 源碼 學習javascript
源碼: nianniuervue
Node.js主要分爲四大部分,Node Standard Library,Node Bindings,V8,Libuv,架構圖以下:java
樹形結構查看,使用 tree 命令node
➜ nodejs git:(master) tree -L 1
.
├── AUTHORS
├── BSDmakefile
├── BUILDING.md
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── COLLABORATOR_GUIDE.md
├── CONTRIBUTING.md
├── GOVERNANCE.md
├── LICENSE
├── Makefile
├── README.md
├── ROADMAP.md
├── WORKING_GROUPS.md
├── android-configure
├── benchmark
├── common.gypi
├── config.gypi
├── config.mk
├── configure
├── deps
├── doc
├── icu_config.gypi
├── lib
├── node.gyp
├── out
├── src
├── test
├── tools
└── vcbuild.bat
複製代碼
進一步查看 deps目錄:python
➜ nodejs git:(master) tree deps -L 1
deps
├── cares
├── gtest
├── http_parser
├── npm
├── openssl
├── uv
├── v8
└── zlib
複製代碼
node.js核心依賴六個第三方模塊。其中核心模塊 http_parser, uv, v8這三個模塊在後續章節咱們會陸續展開。 gtest是C/C++單元測試框架。linux
node.js最初開始於2009年,是一個可讓Javascript代碼離開瀏覽器的執行環境也能夠執行的項目。 node.js使用了Google的V8解析引擎和Marc Lehmann的libev。Node.js將事件驅動的I/O模型與適合該模型的編程語言(Javascript)融合在了一塊兒。隨着node.js的日益流行,node.js須要同時支持windows, 可是libev只能在Unix環境下運行。Windows 平臺上與kqueue(FreeBSD)或者(e)poll(Linux)等內核事件通知相應的機制是IOCP。libuv提供了一個跨平臺的抽象,由平臺決定使用libev或IOCP。在node-v0.9.0版本中,libuv移除了libev的內容。android
libuv是一個高性能的,事件驅動的I/O庫,而且提供了跨平臺(如windows, linux)的API。 隨着libuv的日益成熟,它成爲了擁有卓越性能的系統編程庫。除了node.js之外,包括Mozilla的Rust編程語言,和許多的語言都開始使用libuv。 libuv的官網:docs.libuv.org/en/v1.x/,英文…git
架構圖 github
如今 JS 引擎的執行過程大體是:源代碼 --->抽象語法樹 --->字節碼 --->JIT--->本地代碼。一段代碼的抽象語法樹示例以下:web
function demo(name) {
console.log(name);
}
複製代碼
V8 更加直接的將抽象語法樹經過 JIT
技術轉換成本地代碼,放棄了在字節碼階段能夠進行的一些性能優化,但保證了執行速度。 在 V8 生成本地代碼後,也會經過 Profiler 採集一些信息,來優化本地代碼。雖然,少了生成字節碼這一階段的性能優化, 但極大減小了轉換時間。
JIT
就是即時編譯器,能夠根據字節碼的使用頻率對經常使用的字節碼生成本地機器指令(運行時),而且保存下來
PS:
Tuborfan
將逐步取代Crankshaft
隨着Web相關技術的發展,JavaScript所要承擔的工做也愈來愈多,早就超越了「表單驗證」的範疇,這就更須要快速的解析和執行JavaScript腳本。V8引擎就是爲解決這一問題而生,在node中也是採用該引擎來解析JavaScript。
應用場景:Node的首要目標是提供一種簡單的,用於建立高性能服務器的開發工具 Web服務器的瓶頸在於併發的用戶量,對比Java和Php的實現方式
Node在處理高併發 , I/O密集場景有明顯的性能優點
Chrome V8
引擎的 JavaScript 運行環境,讓JavaScript的執行效率與低端的C語言的相近的執行效率。事件驅動
、非阻塞式 I/O
的模型,使其輕量又高效。npm
,是全球最大的開源庫生態系統。進程是操做系統分配資源和調度任務的基本單位,線程是創建在進程上的一次程序運行單位,一個進程上能夠有多個線程。
爲何JavaScript是單線程?
// websocket
let worker = new Worker('./1.worker.js');
worker.postMessage(10000);
worker.onmessage = function (e) {
console.log(e.data);
}
console.log('main thread')
複製代碼
因而可知瀏覽器是多進程的,而且從咱們的角度來看咱們更加關心瀏覽器渲染引擎
渲染引擎內部是多線程的,內部包含兩個最爲重要的線程ui線程和js線程。這裏要特別注意ui線程和js線程是互斥的,由於JS運行結果會影響到ui線程的結果。ui更新會被保存在隊列中等到js線程空閒時當即被執行.
javascript在最初設計時設計成了單線程,爲何不是多線程呢?若是多個線程同時操做DOM那豈不會很混亂?這裏所謂的單線程指的是主線程是單線程的(node其實也是多線程的),因此在Node中主線程依舊是單線程的。
什麼是多線程?
什麼是單線程?
什麼是進程?
單線程不須要管鎖的問題,這裏簡單說下鎖的概念。例如你們都要去上廁所,廁所就一個,至關於全部人都要訪問同一個資源。那麼先進去的就要上鎖。而對於node來講。就一我的去廁所,因此免除了鎖的問題!
主線程從任務隊列中讀取事件,這個過程是循環不斷的,因此整個的這種運行機制又稱爲Event Loop(事件循環)
queue stack
同步和異步關注的是消息通知機制
補充知識:宏任務 && 微任務(vue $nextTick) 異步方法
// 宏任務 setTimeout
// 同步代碼執行後纔會執行異步
// 根據時間排序,當時間到達後把對應的回調放到隊列
setTimeout(() => {
console.log(1);
setTimeout(() => {
console.log(4);
}, 1000);
}, 1000);
setTimeout(() => {
console.log(2);
}, 2000);
setTimeout(() => {
console.log(3);
}, 3000);
複製代碼
// 宏任務 setImmedate
// setImmedate只兼容ie,默認是低於 setTimeout
setImmediate(function () {
console.log('setImmediate')
})
setTimeout(function () {
console.log('timeout')
}, 4);
console.log(1);
複製代碼
// 宏任務 MessageChannel
let messageChannel = new MessageChannel();
let prot2 = messageChannel.port2;
// postMessage是異步執行的,要等待同步都執行完後纔會被調用
messageChannel.port1.postMessage('111');
console.log(1);
prot2.onmessage = function (e) {
console.log(e.data);
}
console.log(2);
複製代碼
// 宏任務 MutationObserver廢棄了,兼容/性能有問題
let observe = new MutationObserver(function () {
alert('已經dom更新好了')
});
observe.observe(app,{childList:true});
for(let i = 0;i<1000;i++){
app.appendChild(document.createElement('span'));
}
複製代碼
阻塞和非阻塞關注的是程序在等待調用結果(消息,返回值)時的狀態.
同步異步取決於被調用者,阻塞非阻塞取決於調用者
當應用程序須要處理大量併發的輸入輸出,而在向客戶端響應以前,應用程序並不須要進行很是複雜的處理。