JavaScript基礎——你真的清楚JavaScript是什麼嗎?

前端達人公眾號發表於2018-12-12

什麼是JavaScript?

為前端開發,你是否問過自己或者思考過什麼是JavaScript嗎?JavaScript有什麼特點?如果讓你讓一句話高度介紹,你會怎麼說?小編認為,在你想深入一門語言,必須要清楚理解這們語言有什麼特點和其中的執行機制,這是學好一門語言的基礎。

JavaScript是一個單執行緒、非阻塞、非同步、解釋性語言。

單執行緒是個什麼鬼?

有計算機基礎知識的同學可以忽略這部分內容,首先我們來一起了解下計算機基礎知識:執行緒和程式

打個比方,我們去超市購物,結賬的時候會有多個收銀視窗,這樣的好處就是在同一時間完成更多交易處理。這就是計算機常說的多併發概念,作業系統是多併發執行任務,因為它同時執行多個程式。程式是執行環境或正在執行的應用程式的例項。例如,你可能一邊瀏覽著網頁、一邊開啟編輯器寫著程式碼、一邊開著微信聊著天,這都要歸功於計算機能同時執行多個應用程式。

應用程式也是可以處理多併發的,主要是靠執行緒實現的。在其他高階語言,如果你熟悉JAVA就會很容易理解,JAVA可以輕鬆建立多個執行緒處理併發問題,比如同時處理髮出HTTP請求,查詢資料庫或開啟檔案。但是JavaScript是單執行緒的執行環境,它有且只有一個呼叫棧,它每次只能做一件事,程式每次只能執行一段程式碼,這就是單執行緒。單執行緒更通俗的解釋就是——所有任務需要排隊,前一個任務結束,才會執行後一個任務。如果前一個任務耗時很長,後一個任務就不得不一直等著。

什麼是呼叫棧?(call stack)——程式碼在執行過程中,會有一個叫做呼叫棧(call stack)的概念。呼叫棧是一種棧結構,它用來儲存計算機程式執行時候其活躍子程式的資訊。(比如什麼函式正在執行,什麼函式正在被這個函式呼叫等等資訊)。呼叫棧是解析器的一種機制。

首先一起看下一段簡單的程式碼,我們先了解下Javascript是如何執行的?

JavaScript基礎——你真的清楚JavaScript是什麼嗎?
JavaScript基礎——你真的清楚JavaScript是什麼嗎?

首先按照函式呼叫的順序進棧,然後函式執行完了,將值傳給下個函式,出棧,最後將結果輸出。

阻塞是個什麼東東?

什麼是阻塞,沒有嚴格的定義什麼是阻塞。僅僅是指程式碼執行得很慢,比如說console.log不慢,遍歷從1到10億次很慢,你可以可以暫且想想下,如果你用瀏覽器開啟一個網頁,一個網頁肯定會涉及到各種資料的請求,圖片、介面資料、CSS檔案之類,如果JavaScript不想辦法解決優化單執行緒載入資料問題的話,我們瀏覽網頁的體驗可能是這樣的:先出來一個空白頁面,過了一會一張圖片出來了,然後在出來一段文字,過了一會介面資料讀取完了,顯示了一段文字,就這樣網頁像擠牙膏似的一點點的顯示出來,這樣的瀏覽體驗你還會看網頁嗎?。因此更白話點在棧裡表現很慢的東西都叫阻塞。

就像下圖的程式碼,小編寫了一個自己呼叫自己的函式,讓函式進入無線迴圈的過程,造成了呼叫棧裡需要執行大量的函式,模擬了一個阻塞,瀏覽器實在無法承受呼叫之痛,報錯了!

JavaScript基礎——你真的清楚JavaScript是什麼嗎?

祕密武器——非阻塞、非同步回撥

由於JavaScript要解決這個問題,必須要突破單執行緒的瓶頸,“非同步回撥”就成為JavaScript的祕密武器,完美的解決了此問題。非同步回撥讓其擁有了“多執行緒”的能力,其實並不其然,非同步回撥是怎麼解決併發問題,阻塞問題,不知道各位思考過背後的執行進位制嗎?

維基百科是這麼解釋回撥函式的:回撥函式就是一個通過函式指標呼叫的函式。如果你把函式的指標(地址)作為引數傳遞給另一個函式,當這個指標被用為呼叫它所指向的函式時,我們就說這是回撥函式。回撥函式不是由該函式的實現方直接呼叫,而是在特定的事件或條件發生時由另外的一方呼叫的,用於對該事件或條件進行響應。

通俗點回撥是一個函式被作為一個引數傳遞到另一個函式裡,在那個函式執行完後再執行。有點不好理解,小編在說的直白些就是——B函式被作為引數傳遞到A函式裡,在A函式執行完後再執行B。

瞭解完非同步回撥的概念後,我們來看看JavaScript是如何執行的?首先看看下面一張圖

JavaScript基礎——你真的清楚JavaScript是什麼嗎?

在介紹這張圖時,我們先了解下什麼是任務佇列——所有任務可以分成兩種,一種是同步任務(synchronous),另一種是非同步任務(asynchronous)。同步任務指的是,在主執行緒上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務;非同步任務指的是,不進入主執行緒、而進入”任務佇列”(task queue)的任務,只有”任務佇列”通知主執行緒,某個非同步任務可以執行了,該任務才會進入主執行緒執行。

主執行緒從”任務佇列”中讀取事件,這個過程是迴圈不斷的,所以整個的這種執行機制又稱為Event Loop(事件迴圈)。上圖中,主執行緒執行的時候,產生堆(heap)和棧(stack),棧中的程式碼呼叫各種外部API,它們在”任務佇列”中加入各種事件(click,load,done)。只要棧中的程式碼執行完畢,主執行緒就會去讀取”任務佇列”,依次執行那些事件所對應的回撥函式。

“任務佇列”是一個先進先出的資料結構,排在前面的事件,優先被主執行緒讀取。主執行緒的讀取過程基本上是自動的,只要執行棧一清空,”任務佇列”上第一位的事件就自動進入主執行緒。但是,由於存在後文提到的”定時器”功能,主執行緒首先要檢查一下執行時間,某些事件只有到了規定的時間,才能返回主執行緒。

通過下圖將非同步函式的執行步驟視覺化,讓我們更加容易理解上訴文字內容。

JavaScript基礎——你真的清楚JavaScript是什麼嗎?
JavaScript基礎——你真的清楚JavaScript是什麼嗎?
JavaScript基礎——你真的清楚JavaScript是什麼嗎?
JavaScript基礎——你真的清楚JavaScript是什麼嗎?
JavaScript基礎——你真的清楚JavaScript是什麼嗎?
JavaScript基礎——你真的清楚JavaScript是什麼嗎?
JavaScript基礎——你真的清楚JavaScript是什麼嗎?
JavaScript基礎——你真的清楚JavaScript是什麼嗎?

今天的介紹就到這裡,想必大家都十分清楚JavaScript的特點了,我們一起了解什麼是單執行緒、程式、阻塞、呼叫堆疊、非同步回撥、任務佇列、事件迴圈等概念,同時又清楚了JavaScript的執行機制,今天的內容大家都學會了嗎。接下來小編將會給大家介紹JavaScript回撥,承諾和非同步函式,沒理解的同學建議在反覆看下,沒關注公眾號的,麻煩大家關注微信公眾號“前端達人”



相關文章