javascript 預解析

  預解析:瀏覽器

 

  所謂的預解析就是:在當前做用域中,JavaScript代碼執行以前,瀏覽器首先會默認的把全部帶var和function聲明的變量進行提早的聲明或者定義。閉包

1.聲明和定義函數

  var num = 24;
 這行簡單的代碼實際上是兩個步驟:聲明和定義。
  • 聲明:var num; 告訴瀏覽器在全局做用域中有一個num變量了,若是一個變量只是聲明瞭,可是沒有賦值,默認值是undefined。
  • 定義:num = 12; 定義就是給變量進行賦值。

2.var聲明的變量和function聲明的函數在預解析的區別spa

  var聲明的變量和function聲明的函數在預解析的時候有區別,code

   var聲明的變量在預解析的時候只是提早的聲明,function聲明的函數在預解析的時候會提早聲明而且會同時定義。ip

   也就是說var聲明的變量和function聲明的函數的區別是在聲明的同時有沒同時進行定義。作用域

 

3. 預解析只發生在當前的做用域io

  程序最開始的時候,只對window下的變量和函數進行預解析,只有函數執行的時候纔會對函數中的變量很函數進行預解析。console

    console.log(num);    //  undefined
    var num = 24;
    console.log(num);    //  20

    func(100 , 200); 
    function func(num1 , num2) {
     var total = num1 + num2;
     console.log(total);  //  300
    }

    第一次輸出num的時候,因爲預解析的緣由,只聲明瞭尚未定義,因此會輸出undefined;第二次輸出num的時候,已經定義了,因此輸出24。function

    因爲函數的聲明和定義是同時進行的,因此func()雖然是在func函數定義聲明處以前調用的,可是依然能夠正常的調用,會正常輸出300。

 

   做用域鏈

  

  1.先理解如下三個概念:

  1. 函數裏面的做用域成爲私有做用域,window所在的做用域稱爲全局做用域;
  2. 在全局做用域下聲明的變量是全局變量;
  3. 在「私有做用域中聲明的變量」和「函數的形參」都是私有變量

  在私有做用域中,代碼執行的時候,遇到了一個變量,首先須要肯定它是否爲私有變量,若是是私有變量,那麼和外面的任何東西都沒有關係,若是不是私有的,則往當前做用域的上級做用域進行查找,若是上級做用域也沒有則繼續查找,一直查找到window爲止,這就是做用域鏈。

 

  

  2.當函數執行的時候,首先會造成一個新的私有做用域,而後按照以下的步驟執行:

  1. 若是有形參,先給形參賦值;
  2. 進行私有做用域中的預解析;
  3. 私有做用域中的代碼從上到下執行

  函數造成一個新的私有的做用域,保護了裏面的私有變量不受外界的干擾(外面修改不了私有的,私有的也修改不了外面的),這也就是閉包的概念。

  

    console.log(total); 
    var total = 0;
    function func(num1, num2) {
     console.log(total); 
     var total = num1 + num2;
     console.log(total);
    }
    func(100 , 200);
    console.log(total);

  以上代碼執行的時候,第一次輸出total的時候會輸出undefined(由於預解析),當執行func(100,200)的時候,會執行函數體裏的內容,此時func函數會造成一個新的私有做用域,按照以前描述的步驟:

  1. 先給形參num一、num2賦值,分別爲100、200;
  2. func中的代碼進行預解析;
  3. 執行func中的代碼

  由於在func函數內進行了預解析,因此func函數裏面的total變量會被預解析,在函數內第一次輸出total的時候,會輸出undefined,接着爲total賦值了,第二次輸出total的時候就輸出300。 由於函數體內有var聲明的變量total,函數體內的輸出total並非全局做用域中的total。

    最後一次輸出total的時候,輸出0,這裏輸出的是全局做用域中的total。

 

  console.log(total); 
  var total = 0;
  function func(num1, num2) {
   console.log(total); 
   total = num1 + num2;
   console.log(total);
  }
  func(100 , 200);
  console.log(total);

3.全局做用域下帶var和不帶var的區別

在全局做用域中聲明變量帶var能夠進行預解析,因此在賦值的前面執行不會報錯;聲明變量的時候不帶var的時候,不能進行預解析,因此在賦值的前面執行會報錯。

  console.log(num1);    // undefined 
  var num1 = 12;    

  console.log(num2);    // 報錯 : num2 is not defined.
  num2 = 12;      

至關於給window增長了一個num2的屬性名,屬性值是12;
  至關於給全局做用域增長了一個全局變量num1,可是不只如此,它也至關於給window增長了一個屬性名num,屬性值是12;

  num2 = 12;var num1 = 12;

問題:在私有做用域中出現一個變量,不是私有的,則往上級做用域進行查找,上級沒有則繼續向上查找,一直找到window爲止,若是window也沒有呢?

        獲取值:console.log(total); --> 報錯 Uncaught ReferenceError: total is not defined.

     設置值:total= 100; --> 至關於給window增長了一個屬性名total,屬性值是100

 

  function fn() {
   // console.log(total); // Uncaught ReferenceError: total is not defined
   total = 100;
  }
  fn();
  console.log(total);

 

  注意:JS中,若是在不進行任何特殊處理的狀況下,上面的代碼報錯,下面的代碼都再也不執行了!

 

預解析中的一些變態機制:

  1 無論條件是否成立,都要把帶var的進行提早的聲明

    if (!('num' in window)) { 
     var num = 12;
    }
    console.log(num); // undefined

   JavaScript進行預解析的時候,會忽略全部if條件,由於在ES6以前並無塊級做用域的概念。
以上示例會先將num預解析,而預解析會將該變量添加到window中,做爲window的一個屬性。
那麼 'num' in window 就返回true,取反以後爲false,這時代碼執行不會進入if塊裏面,num也就沒有被賦值,
最後輸出爲undefined。
console.log(num)

 

  2 只預解析「=」左邊的,右邊的是值,不參與預解析  

    fn(); // -> undefined();  // Uncaught TypeError: fn is not a function
    var fn = function () {
     console.log('ok');
    }

    fn(); -> 'ok'
    function fn() {
     console.log('ok');
    }
    fn(); -> 'ok'
  建議:聲明變量的時候儘可能使用var fn = ...的方式。

 

  3 自執行函數:定義和執行一塊兒完成 

  (function (num) {
    console.log(num);
  })(100);

 

  自執行函數定義的那個function在全局做用域下不進行預解析,當代碼執行到這個位置的時候,定義和執行一塊兒完成了。

 

    補充:其餘定義自執行函數的方式

 

      ~ function (num) {}(100) 
      + function (num) {}(100) 
      - function (num) {}(100) 
      ! function (num) {}(100)

 

  4 return下的代碼依然會進行預解析

    function fn() {        
     console.log(num); // -> undefined
     return function () {    

     };        
     var num = 100;     
    }         
    fn();

  函數體中return下面的代碼,雖然再也不執行了,可是須要進行預解析,return中的代碼,都是咱們的返回值,因此不進行預解析。

 

  5 名字已經聲明過了,不須要從新的聲明,可是須要從新的賦值

    var fn = 13;          
    function fn() {         
     console.log('ok');        
    }             
    fn(); // Uncaught TypeError: fn is not a function    


 

  eg: 經典題目 

    fn(); // -> 2            
    function fn() {console.log(1);}       
    fn(); // -> 2            
    var fn = 10; // -> fn = 10        
    fn(); // -> 10() Uncaught TypeError: fn is not a function       
    function fn() {console.log(2);}       
    fn();
歡迎留言,歡迎踩踩踩!
相關文章
相關標籤/搜索