JavaScript的"併發模型"是基於事件循環的,這個併發模型有別於Java的多線程, javascript的併發是單線程的。javascript
Javascript 中有個重要一塊,Event Loop,能把單線程的 JavaScript 使出 多線程的感受。html
"Event Loop是一個程序結構,用於等待和發送消息和事件。(a programming construct that waits for and dispatches events or messages in a program.)"前端
簡單的說,就是在程序中(不必定是瀏覽器)中跑兩個線程,一個負責程序自己的運行,做爲主線程; 另外一個負責主線程與其餘線程的的通訊,被稱爲「Event Loop 線程" 。 每當遇到異步的 setTimeOut ,setInterval 這些異步任務,交給 EventLoop 線程,而後本身日後運行,等到主線程運行完後,再去 Event Loop 線程拿結果。java
這種模型人稱 "asynchronous" 或 "non-blocking" 模行。 web
我簡單的畫了一個 javascript 的執行圖,咱們經過圖,逐步分析.數組
棧瀏覽器
函數調用時所用的執行環境棧網絡
當js方法被調用時,會進入一個執行環境(execution context),若是有另一個方法被調用了(或者自身遞歸調用),會新建一個新的執行環境,而且代碼的執行會進入到這個新的執行環境.函數調用返回的時候從新回到原來的執行環境. 由此,代碼執行的過程便造成了一個執行環境棧,江湖人稱 "stack";多線程
execution context 是一個由ECMA定義的抽象的概念,全部的javascript代碼都是在 execution context 執行環境中執行的.閉包
全局執行環境是最外層的一個執行環境.在Web瀏覽器中,全局執行環境認爲是window對象.所以全部全局變量和函數都是做爲window對象的屬性和方法建立的.
全局的代碼(inline中執行的代碼,一般包括js文件,html頁面,加載的)在全局執行環境中執行.每一個方法調用都有一個與之關聯的執行環境.
某個執行環境中的全部代碼執行完畢後,該執行環境被銷燬,保存在其中的全部變量和函數定義也隨之銷燬. 全局執行環境直到頁面關閉時才被銷燬.
每一個函數都有本身的執行環境,當執行流進入一個函數時,函數的執行環境就會被推入環境棧中. 而在函數執行以後,棧
將其環境棧彈出,把控制權返回給以前的執行環境.js中的執行流正是由這個方便的機制的控制着.
當代碼在一個執行環境中執行時,會建立變量對象的一個做用域鏈(scope chain).
做用域鏈的用途,確保當前執行環境能有序(不明白就接着看)的訪問所能獲取的變量和函數.
做用域鏈的前端,始終都是當前執行的代碼所在的執行環境的變量對象.
即當前做用域沒有,外層做用域兜着.
執行環境被建立時會有次序的進行一些工做.
首先,在方法的執行環境中,Activation 活動對象被建立.活動對象另有一套實現機制.能夠認爲是對象,但又很特殊,沒有prototype,不能在代碼中直接引用到.
下一步,在方法調用建立執行環境的時候,會建立 argument 對象(類數組對象,含有傳進來的參數,length,callee),活動對象中會有一個"argument"同名屬性來引用這個argument對象.
再下一步,執行環境會被指定一個做用域.做用域由承載對象的列表(或鏈)組成.當代碼在一個執行環境中執行,會建立變量對象的一個做用域鏈(scope chain).做用域鏈的前端,始終都是當前執行的代碼所在執行環境的變量對象(和上面提到的 Activation 活動對象是一個對象). 若是這個環境是函數(javascript 環境只有函數和全局環境這兩種), 則將其活動對象做爲變量對象使用,活動對象在最開始時只包含一個arguments對象.
做用域鏈中下一個變量對象來自當前代碼的外層環境.以此類推,直到到達最外層的全局執行環境,這樣便構成了一條從底到上的做用域鏈.全局執行環境的變量對象始終都是做用域鏈的最後一個對象.
標示符解析是沿着做用域鏈一級一級的搜索標示符的過程.搜索過程始終都是從做用域鏈的最前端開始,而後主逐級向後回溯.
當進入執行環境(代碼即將但仍未執行時),變量對象也就是活動對象已經包含了如下這些屬性:
函數的全部形參,由名稱和對應值組成變量對象的屬性.
執行環境所在函數內部的全部子函數聲明,由名稱和對應值(函數對象 function object)組成變量對象的屬性.若是變量對象內已存在同名屬性,那麼會被替換全部的變量聲明.
由名稱和對應值(undefined)組成變量對象的屬性,若是變量名稱跟已經聲明的形式參數或函數相同,則變量聲明不會干擾已經存在的這類屬性.
須要說明的是,每一個執行環境都有this,this的值取決於調用者和所執行代碼的類型,this值是在進入執行環境時就已經肯定的. 取值與執行環境相關聯,而且在執行環境運行期間是不能被修改的.
this 執行上下文中的一個屬性.
在全局代碼中,this始終是全局對象自己.
在一般的函數調用中,this是由函數的調用者提供的,即被調用函數的父執行環境提供的.this值取決於函數調用的方式.
堆是一個對象互聯的網絡。用數學術語說就是「圖」。圖由節點及其之間的邊構成。節點和邊都是可被標記的:節點(對象)用對象構造器的名稱標記,邊則由屬性名稱標記。
從一個對象到另外一個的邊序列被叫作路徑(path)。一般咱們只對那些不重複通過同一節點兩次的簡單路徑(simple path)感興趣。
咱們把垃圾收集器根節點到某個指定對象的路徑叫作retaining path。若是不存在這樣的路徑,則該對象被稱做沒法達到的(unreachable),應在垃圾收集過程當中被處置。
在web瀏覽器中,當事件發生時若是該事件有相應的監聽器,則該消息會被及時的加進消息隊列,若是沒有監聽器,該事件會被丟失.
javascript運行時伴隨一個待處理的消息(也就是任務)隊列,每一個消息都關聯了相關的處理函數,當函數的執行棧爲空時,即當前沒有正在執行的函數,那麼,隊列會從中挑出一個去處理.處理過程包括調用相關的函數(不用擔憂處理函數的做用域問題,由於在javascript中,做用域是詞法化做用域,是方法在定義的時候已經肯定的,與調用無關.做用域鏈建立早於方法調用,得益於此,咱們方能使用閉包),處理完成後,若是棧爲空,則再次嘗試從隊列中挑選可處理的事件或任務. 從中能夠很容易窺探到 setInterval,setTimeout 是怎麼進行異步執行的了.
這,就是事件循環,event loop.