Js 運行機制深層剖析

」js是一門單線程的語言,js最大的特性是異步「,這些說法已經見慣不怪了,然而不瞭解js的解釋執行始末,這些概念也就只是聽聽,真正遇到問題的時候,也只能一臉懵逼而已~javascript

1、Event Loop 機制/異步原理

eventLoop.png

因爲 Js 是一門單線程的語言,爲了實現異步特性,必須有一種行之有效的機制,Event Loop 就是這種機制java

說 Js 是一門單線程語言,指的是它只有一個用戶執行線程,同一時刻只能執行一個任務,在你看不見的地方,還須要有不少其它線程/進程來調度ajax

同步的狀況

當程序開始運行的時候,將默認執行全局執行上下文(將在後文中說明)中的代碼,若是遇到同步函數,那麼將把當前執行上下文壓入棧中,進入同步函數的執行上下文執行(假若在函數中又遇到了其它同步函數,將持續此過程),當函數執行完成,執行線程將從消息線程中pop一個執行上下文進行執行網絡

global() {

        /** * 定義 */
        EC3() {

        }
        EC2() {
            EC3(); //調用EC3
        }
        EC1() {
            EC2();//調用EC2
        }

        //調用EC1
        EC1();
    }複製代碼

異步的狀況

若是當前執行上下文遇到了異步操做,那麼它向事件監聽線程或者計時線程發出通知以後,將繼續執行,此時有兩種狀況:
a) 計時函數,如:閉包

setTimeout(func, 1000);複製代碼

此時,異步通知將發給計時線程(Timer),一秒後,計時線程將會把 func 函數的執行上下文壓入消息線程堆棧,執行線程在處理完當前執行上下文的時候從消息線程堆棧中 Pop 出 func 的執行上下文進行執行(若是在此期間沒有別的執行上下文入棧的話)異步

因此,setTime(func, n); 並不能保證,在 n 毫秒以後 func 能被執行,這還得看執行線程當前在幹什麼了函數

b) 事件監聽線程,若是發起的是一個異步io操做,如發起一個網絡請求:oop

$.ajax(url, params, callback);複製代碼

那麼異步事件將發給事件監聽線程去監聽,一旦網絡請求完成,一樣的,事件監聽線程將把 callback 的執行上下文入棧,等待執行線程的召喚ui

2、js的執行上下文

上文反覆提到執行上下文,下面就對執行上下文進行一個深刻剖析this

如何理解執行上下文

所謂執行上下文,就是 Js 執行的時候的一個運行環境/做用域(scope),有以下幾種狀況:

  1. 全局執行上下文/做用域:js代碼的默認執行環境(只有一個)
  2. 函數執行上下文/做用域:每一個函數對應的執行環境(無限多個)
  3. eval 代碼執行上下文:使用 eval 執行的腳步的執行環境
/** * 全局執行上下文/做用域 */
        console.log('在全局環境中執行')

        function hello() {  //hello函數執行上下文/做用域
            var say = 'hello';

            function world() { //world函數執行上下文
                ...
            }
        }複製代碼

全局做用域中的方法、變量,能夠被其它任何函數做用域所訪問,函數做用域中的方法變量,在子函數做用域中能夠訪問,外部沒法直接訪問

經過函數返回的子函數去訪問函數做用域的私有變量,也就造成了閉包

執行堆棧

也就是上文提到的 Event Loop,該線程以的形式保存執行上下文,函數執行上下文的入棧出棧的過程,使得js得以在單線程的狀況下,實現異步特性

執行上下文的建立與執行

每一次函數被調用,js解釋器都會爲之建立新的上下文,此時能夠分爲兩個階段:
a) 上下文的建立階段:函數被調用,但還沒有開始執行(代碼分析預處理階段),此時會爲執行上下文建立做用域鏈,建立變量、函數和參數以及求this的值

executionContextObj = {
        scopeChain: { /* 變量對象(variableObject)+ 全部父執行上下文的變量對象*/ }, 
        variableObject: { /*函數 arguments/參數,內部變量和函數聲明 */ }, 
        this: {} 
    }複製代碼

特別的,變量提高就是在這個階段發生的

b) 執行階段:指派變量的值和函數的引用並解釋執行代碼

下面再用僞代碼的形式來描述一下這個過程:

//函數被調用
    1. 建立執行上下文
        a) 建立做用域鏈
        b) 建立變量、函數和參數
        c) 求this2. 開始執行在執行上下文上 執行
        ...
        a) 遇到同步函數
        b) 當前執行上下文入棧
        c) 重複以上過程
        ...
    3. 執行完成,往上一層 執行上下文  返回數據
    4. 從執行上下文棧pop出一個新的執行上下文執行複製代碼

2、總結

瞭解 Js 解釋器一些底層原理,是很是有必要的,當程序的運行結果跟你的預期結果不同的時候,甚至看起來很詭異的時候,如何去解釋,就很體現能力了

更多精彩,邀您關注~~

相關文章
相關標籤/搜索