1. 程式人生 > 其它 >js預解析

js預解析

js的預解析:

為什麼我們用var定義一個變數a,在這行程式碼之前列印a卻不報錯,而是得到undefined?又或者函式為什麼可以隨意放置,在哪裡呼叫都可以應用,這是什麼原理?

其實這樣的情況,都是因為JavaScript中有一種機制,就是:“預解析機制”。

1.1   預解析原理

簡單來說,預解析就是——在當前作用域下,js在執行之前,會把帶有var和function關鍵字宣告的變數先宣告,並在記憶體中安排好。然後再從上至下解析js語句。

  var 預解析

    js 在正常解析之前,會快速的把 script (或者 funciton )中的 var 宣告及宣告的名字,提升到程式碼塊(作用域)的最前面。

  function 預解析

    js 在正常解析之前,會快速的把 script (或者 funciton )中的 function 及內容提升到程式碼塊(作用域)的最前面,跟在 var 宣告之後。

1.2 var宣告

通過var宣告的變數,進行預解析的時候:先宣告變數,不管變數有沒有賦值,宣告時都賦值為undefined。

  事例:

    console.log(a); //undefined

    var a = 1;

    console.log(b); //undefined

    var b = function(){}//注意,這是個函式表示式

  事例:

  第1步:

    console.log(a);// undefined,此時讀取變數a

    var a = 10;

//解釋:在執行程式碼之前,js會預解析程式碼,程式碼中如果有變數宣告和函式宣告的話,那麼便將變數宣告和函式宣告置頂;

也就是說,以上程式碼在js執行中,是如下執行的:

  var a;//變數宣告置頂設定,但不賦值

  console.log(a); //undefined

  a = 10;//變數在此處賦值。

  第2步:

    console.log(a); //undefined

    console.log(b); //undefined

    var a = 10;

    var b = function(){}//同樣是var宣告的函式進行置頂設定,但是不賦值;

  第3步:

    var a = 10;

    var a = function(){};

    console.log(a); //function (){}

相當於:

    var a = 10;

    a = function(){};

    console.log(a); //function (){}

    var定義的a,是同一個a,不會產生不同的變數,只是讓a改變了值,由數值變成了函式。

反之亦然,

    var a = function(){};

    var a = 10;

    console.log(a); //10

var宣告按照賦值順序執行,同名的var宣告,後者的宣告不會產生新變數,只是值會覆蓋前者。

1.3 function宣告

  js 在正常解析之前,會快速的把 script (或者 funciton )中的 function 及內容提升到程式碼塊(作用域)的最前面,跟在 var 宣告之後。

  function進行預解析的時候,不僅是宣告而且還定義了函式體,在記憶體中會開闢一塊記憶體空間,儲存的是函式體的字串,但是程式碼不會執行。只有函式呼叫以後才執行。

  事例:

  第1步

    console.log(a); //列印結果不是a,而是函式a()的方法體!證明函式a跟在var宣告之後被宣告的。

    var a = 10;

    function a(){return 100;};

其實相當於:

    var a;

    function a(){return 100;};

    a = 10;

  第2步

    var a = 10;

    function a(){return 100;};

    console.log(a); //10

解釋請看上一步的程式碼解釋。

注意:

(1) 同名的var宣告和同名的函式宣告(不是函式表示式,用function宣告的函式),不管二者書寫先後順序,函式宣告會覆蓋掉var宣告的變數(因為它的置頂順序低於var宣告);

(2) 同名的var宣告,後者會被忽略;

(3) 同名的函式宣告,後者會覆蓋前面的(這只是函式宣告按自上而下的順序執行罷了)。

課堂練習,思考一下結果:

var a = 10;

function a(){return 100;};

var a = function (){return 10000;};

console.log(a);

注意問題:

(1) 預解析,不會超出script標籤,前面訪問不到後面的定義

<script>console.log(a) // 報錯</script>

<script>function a(){}</script>

(2) 後面script標籤中可以訪問呢前面script標籤中的js,因為js是從上至下執行的。

(3) 函式內部同名變數的宣告高於傳入的同名引數

var a = 10;

function fn(a){

var a = 20;

console.log(a);//20

}

fn(a);

(4) 函式內參數的宣告高於外部同名變數的宣告。

var a = 10;

function fn(a){

console.log(a);//100

}

fn(100);

1.4 預解析應用

var a = 1;

function b(){

a = 10;

return;

function a(){

console.log(a);

}

}

b();

console.log(a);

思考,列印結果?

注意:預解析既可置頂提升變數也可以提升函式。

程式碼會按照如下情況執行:

var a;

function b(){//注意,函式也會提升

function a(){//注意,函式也會提升,而不是被return結束掉了。

console.log(a);

}

a = 10;//注意,作用域鏈的應用,它的查詢順序是由內而外,就近原則!所以雖然函式a沒有被呼叫過,但是這裡的賦值,是賦值給b函式中的a函式的!

return;

}

a = 1;

b();

console.log(a);

課堂練習:

  var a;

  function a(){

  console.log(10);

  }

  console.log(a);

  function a(){

  console.log(10);

  }

  var a;

  console.log(a);

  function a() {

  var b = 1;

  }

  console.log(a);

  var a = function() {

   var b = 2;

  };