你真的理解javascript中的預編譯麼?

連眉ss發表於2019-04-10

深刻理解JS中的“預編譯階段”和“執行階段”

什麼是“預編譯”

1、javascript是一種解釋型語言,例如C、Java等強型別語言中的編譯階段,

   它是沒有這個步驟的,因此,javascript中有了類似於強型別語言編譯階段的步驟---預編譯,

   同時,我們需要知道,js引擎不是逐行的解釋程式碼,而是按照程式碼塊解釋,

   即,以<script></script>標籤為塊,進行解釋,

   另外,我們還需要知道的是,預編譯過程是在執行過程的前一刻發生並執行完畢,

   也就是說到了執行階段的時候,預編譯的過程已經完成了;

2、我們在程式設計過程中有自己的一套語言和語法,同樣的,計算機也有自己的一套,

   面對程式碼,我們可以清晰的知道它的執行方式和順序,但是,計算機不一定能懂,

   因此,預編譯也是讓計算機“認識”我們所寫的程式碼的過程,

   例如下文將會提到的,AO物件,就是JS引擎在執行程式碼過程中重要的依賴;

複製程式碼

javascript中的宣告方式

在js中宣告的方式有兩種,變數宣告和函式宣告

即,var(let、const)和function

瞭解這個對下面預編譯的過程有重要幫助
複製程式碼

下面隨著程式碼分析預編譯的過程

  <script>

    function test(a) {

      console.log(a);

      var a = 123;
      
      console.log(a);

      function a() {

      };

      console.log(a);

      var b = function () {

      };
      
      console.log(b);

      function d() {

      };
    }

    test(1);

  </script>


    先自己嘗試著寫出這段程式碼執行後的結果是什麼?

複製程式碼

函式預編譯發生在函式執行的前一刻

第一步 建立AO物件

AO物件:官方翻譯為執行期上下文

在上述程式碼中,script標籤內,簡單的看,有一個函式的定義及該函式的傳參執行兩個步驟,

js引擎掃描完程式碼,在 “test(1)”這行程式碼將要執行的前一刻,開始了“預編譯”的過程,

建立js引擎所認識的AO物件,該物件中存放著變數和方法的宣告,僅供js引擎進行讀取等操作,

每個函式都會有自己的AO物件,此時這個物件是屬於test這個函式的,

也就是說這個AO物件就是test函式的作用域

AO {

}
複製程式碼

第二步 找形參和變數宣告,將變數和形參名作為AO的屬性名,值為undefined

形參為a,作為鍵新增到AO物件中,值為undefined;

變數宣告有兩個,var a;var b;

注意,由於宣告的變數a和形參相同,那麼只需要新增一次就可以了,

那麼AO物件就變為如下:

AO {
  a:undefined,
  b:undefined
}
複製程式碼

注意: 1、相同的變數名作為一次屬性名 2、變數宣告,不是函式宣告

第三步 將實參值和形參統一

test(1) ===>  實參為1,將1賦值給AO物件中的屬性a

AO {
  a:1,
  b:undefined
}
複製程式碼

第四步 在該函式體裡面找函式宣告,值為函式體

函式宣告有兩個

function a() {}

function d() {}

其中a函式宣告的函式名與變數宣告中的變數名相同,在此步驟中,函式宣告優先順序高於變數宣告,

將會覆蓋之前的變數宣告,同時,新增d的函式宣告,

此時AO物件變為:

AO {
  a: function a() {},
  b: undefined,
  d: function d() {}
}

複製程式碼

注意: function xxx() {} 為函式宣告 var xxx = function () {} 是函式表示式,是變數宣告,不是函式宣告,

到此時,預編譯的過程完成,下面是函式執行過程

1、執行console.log(a);

此時從AO物件中可以知道,a的值為 function a() {}

故,這碼執行的結果為 fucntion a() {}
複製程式碼

2、程式碼繼續執行

var a = 123; 執行完該行程式碼之後AO物件變為

AO {
  a: 123,
  b: undefined,
  d: function d() {}
}

故,執行的結果為 123
複製程式碼

3、繼續執行的程式碼為函式宣告

在預編譯過程的時候已將其提升,AO物件不變

故,此行程式碼執行的結果為 123
複製程式碼

4、繼續執行

變數b在預編譯過程僅僅提升宣告,並沒有對其賦值,此時對變數b進行賦值

AO物件變為:

AO {
  a: 123,
  b: function () {},
  d: function d() {}
}

故,此行程式碼執行的結果為 function () {}
複製程式碼

5、程式碼就往下執行,這部分程式碼在預編譯過程時候處理過,至此,函式執行完畢

上述程式碼執行的結果為

function a() {}
123
123
function () {}
複製程式碼
練習題

  function fn(a, b) {
    console.log(a);

    console.log(b);
    
    var b = 234;

    console.log(b);

    a = 123;

    console.log(a);

    function a() {

    };

    var a;

    var b = function () {

    }

    console.log(a);

    console.log(b);
  }

  fn(1);
複製程式碼

相關文章