預編譯與變量提高詳細講解

前言:做爲解釋性語言的JS,在執行前會先進行預編譯。理解了預編譯,JS中的變量提高就能夠更快的搞懂啦!javascript

1、預編譯前奏

在講解預編譯前,先講解個知識點。敲黑板啦!小夥伴們不要光看,本身打打代碼才知道。java

  1. imply global:暗示全局變量:若是變量未經聲明就賦值,此變量歸全局對象全部,即歸window全部。以下所示,即window.a = 10

這個window究竟是什麼,其實它就是一個全局對象,提供了一個全局做用域。在全局做用域聲明變量,就是爲全局對象添加屬性。即在window對象中添加了一個屬性a,其值爲10。bash

<script type="text/javascript">
    a = 10;
    console.log(a);//訪問這個a實際上就是訪問window.a
<script>
複製代碼

爲了更好地理解,再來看一下下面代碼。 window.a和window.b會輸出什麼呢?

<script type="text/javascript">
 function test() {
    var a = b = 123;
 }
 test();
<script>
複製代碼

步驟:

  • b = 123;b是沒有聲明就賦值的,因此根據上面的imply global暗示全局變量,是一個全局變量,是window的一個屬性,因此能夠經過window.變量名訪問
  • var a = b;由於它在函數裏,不是全局變量,是局部變量,因此在window中沒有。
  1. 一切聲明的全局變量,全是window的屬性。
<script type="text/javascript">
    var b = 20;
    console.log(b);
<script>
複製代碼

1、預編譯

看一段代碼函數

  • b輸出的結果的undefined,那麼undefined是什麼意思呢?是var聲明瞭變量可是未對它進行初始化。因此先讓這個變量初始化爲undefined,也就是先讓b=undefined直到真正賦值階段時纔是20。
  • 而a輸出的是Uncaught ReferenceError: Cannot access 'a' before initialization。翻譯過來的意思就是沒法在初始化以前訪問a。
<script type="text/javascript">
    //var
    console.log(b);//undefined
    var b = 20;
    //let
    console.log(a);//Uncaught ReferenceError: Cannot access 'a' before initialization
    let a = 10;
<script>
複製代碼

爲何varlet二者不同呢?先來說講varui

  • var b = 20;包括兩個步驟:
  1. 聲明:var b;
  2. 賦值:b = 20;
<script type="text/javascript">
var b;//該聲明提高了,提高到做用域的頂部。
console.log(b);//undefined
b = 20;
<script>
複製代碼

var的提高實際上是建立初始化都提高了。也許你又會問那爲何初始化的值是undefined。這和執行前發生的預編譯有關。spa

  • 此提高過程在預編譯中進行:
  1. 函數:預編譯發生在函數執行前一刻
  2. 整個js :預編譯發生在全局執行的前一刻

函數預編譯步驟

先看下面的代碼到底輸出什麼?既有函數又有變量,並且還同名,那變量的聲明提高和函數提高到底誰的優先級高呢?想必是很懵逼吧,不要緊,跟着我一塊兒來一步一步理解下。翻譯

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);
複製代碼
  1. 建立AO對象:
AO{}
複製代碼
  1. 找到形參和變量聲明,將形參和變量聲明做爲AO的屬性名,屬性值爲undefined:
AO{
    形參或變量聲明:undefined
}
也就是
AO{
    a:undefined,//這裏形參function test(a)和變量var a都是,寫一次就行了
    b:undefined
}
複製代碼
  1. 找到實參的值,賦值給形參
AO{
    形參或變量聲明:實參
}
也就是
AO{
    a:1,
    b:undefined
}
複製代碼
  1. 函數體裏面找到函數聲明, 值爲函數體
AO{
    形參或變量聲明:函數體
}
也就是
AO{
    a:function a() {},//函數聲明
    b:undefined,
    //注意了 var b = function () {}這個不是函數聲明,是函數表達式,因此b的值仍是undefined
    d:function d() {}//函數聲明
}
複製代碼
  • 此時AO對象已經建立完,那麼就開始執行函數,開始打印。

注意:要打印的值都從預編譯後的AO對象中取值code

function test(a) 
 {      
    //1.輸出function a() {}
         console.log(a);
    /*2. 預編譯時已經變量提高了,可是賦值尚未操做。因此此時AO對象中a的值改變爲123
        AO{
        a:123,
        b:undefined,
        d:function d() {}
    }
    */
         a = 123;
    //3.輸出:123
         console.log(a);
         function a() {}
    //4.輸出123
         console.log(a);
    /*5.改變AO對象中b的值
    AO{
        a:123,
        b:function () {},
        d:function d() {}
    }
    */
         var b = function () {}
    //6.輸出function () {}
         console.log(b);
         function d() {}
 }
複製代碼

實際輸出結果和分析的同樣。棒棒噠!cdn

3、提高

理解了預編譯後,想必var的變量提高弄明白了吧!var提高指的是建立初始化都被提高了。因此下面代碼輸出的結果纔是undefined對象

console.log(a);
var a = 20;
複製代碼
console.log(a);//Uncaught ReferenceError: Cannot access 'a' before initialization
let a = 10;
複製代碼

對於上面的結果也許你會說:那let不就沒有提高。這就錯了,其實它是有提高的,只不過提高的只有建立過程,初始化時沒有提高的。看碼

let a = 1;
         {
             console.log(a);
             let a = 2;
         }
複製代碼

若是let不存在提高,那麼應該輸出1。可是卻報錯 Uncaught ReferenceError: Cannot access 'a' before initialization。這就說明let存在提高。只是被存在「暫存死區」中。只有當初始化後才能被引用。

4、總結

  1. var :【建立】和【初始化】都被提高
  2. function:總體提高,也就是【建立】【初始化】【賦值】都被提高
  3. let:【建立】過程提高,但初始化沒有提高。
相關文章
相關標籤/搜索