深入學習JavaScript之提升

 或許你在編寫JavaScript時,會注意到這麼一個現象,聲明的出現與其作用域內的操作位置有微妙的關係,

 有人認爲JavaScript引擎執行代碼是從上到下一行一行執行的。實際上這不全對

例如:

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

   如果按照JavaScript引擎是一行一行代碼解釋的話,先賦值再聲明再輸出,因爲var a在a=2後面,理所當然認爲a被重新賦值了,那麼輸出結果是undefined,但是很遺憾並不是,輸出結果爲2。

 

我們將它們之間的位置調換下會得到:

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

  如果按照對之前代碼進行的分析,那麼此處會輸出  "2"  或者這樣思考,a未定義前就使用,那麼此處會輸出"ReferenceError"。     但是結果卻是  "undefined"  ,爲什麼???

  這其實就是“先有雞還是先有蛋的問題”即先聲明在使用還是先使用在聲明的問題

 

1.2編譯器

  編譯器在JavaScript中的作用是進行詞法分析/語法分析以及生成代碼。編譯的一部分工作----在詞法分析中產生詞法域,這時候會得到詞法域以及各類聲明。

    包括函數和變量在內的所有聲明在任何代碼被執行之前首先被處理

  當看到"var a=2"時你以爲這會是一句聲明語句,其實不然,這在JavaScript看來是兩種聲明,一種是聲明語句"var a;",另一種是賦值語句"a=2;",聲明語句在編譯時就已經完成,而賦值語句則要在原地等待執行。

     之前我們的代碼它的執行過程如下

  //先編譯再執行
  a=2;     //等待執行
   var a;    //編譯時先執行此聲明
   console.log(a);   

  如上所示,因爲編譯器首先是編譯此代碼隨後再執行,所以先執行  var a  ;在執行  a=2 ;最後執行  console.log(a);所以輸出爲2。

  如果按照從上到下的執行順序,那麼上面代碼可變爲

  var a;   //先執行聲明語句
   a=2;
   console.log(a);

那麼,之前講的這個代碼片段

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

可變爲

var a;

console.log(a);

   a=2;        

  在這裏JavaScript將  var a=2  分解爲  var a  和   a=2。var a  首先被執行,所以上升到第一位。

--------------------------------在上面例子中,var a  聲明語句總是被提升到第一位,所以叫提升

   提升過程有兩點需要注意的

①函數聲明也是可以提升的,

   例如

   foo();         //foo()函數能夠正常運行
   function foo(){    //函數聲明發生了提升

    console.log(a);        //ReferenceError

         var a=2;

   }

  函數聲明提升,所以第一行調用可以正常執行。

②提升只會提升聲明,而不會提升邏輯(賦值等操作)

  如上面的  var a  被提升(是在foo(...)函數中的最上方,而不是整個程序),剩下  a=2  在原地。

 

  這時候我們稍加對上面程序進行修改,你就會發現問題

   foo();         //TypeError而不是ReferenceError
   var foo=function bar(){    //函數聲明發生了提升

    console.log(a);        //ReferenceError

         var a=2;

   }

  var foo=function bar(){...}   中同樣被分成兩部分   var foo  以及  foo=function bar(){....},聲明被提升到最頂部,賦值操作沒有提升。

因此foo()調用沒有發生ReferenceError錯誤。但是foo()並沒有被賦值(如果它是一個函數聲明而不是一個函數表達式,那麼它就必須要賦值,此處它沒有賦值,所以默認的值爲undefined),foo()由於對undefined進行函數調用而導致非法操作,拋出TypeError。

  

1.3函數優先

  函數聲明和變量聲明假設都在同一個作用域中,那麼是函數優先還是變量聲明優先呢??

   我們看一個例子

 foo();    //1  證明是函數聲明優先
      var foo;
      function foo(){
          console.log(1);
      }
      foo=function () {
          console.log(2);
      }

  在以上代碼中,我們可以看到,輸出結果爲1,從代碼執行上來看,如果是變量聲明優先的話,那麼foo()會輸出2,如果是函數優先那麼會輸出1。

  這個代碼可以化成這樣的形式:

function foo(){
    
    console.log(1);
  
  };

   foo();
 
   foo=function(){

     console.log(2);     

     };

  注意,var foo  儘管出現在  function  foo(...)聲明之前,但是它是重複的聲明(因此被忽略了),因爲函數聲明會被提升到普通變量之前。---------------------這告訴我們,在一個作用域中重複定義是糟糕的,它可能會出現各種各樣的問題!!!!

 

總結:

    作用域中的聲明將會提升到頂部的過程------我們稱之爲提升。

     提升分爲兩種,一種是變量提升,一種是函數提升,當兩者同在一個作用域中時,函數聲明先提升,隨後纔是變量提升-------兩者都是提升到當前作用域的頂部。

    提升的過程中只會提升聲明,而不會提升操作語句,例   "var    a=2"   將會被分成兩部分:" var   a"       "  a=2  ",當發生提升時只有  " var  a"被提升到當前作用域的頂部。  "a=2"將在原地等待執行。

    避免在同一個作用域中重複定義,特別是當普通的var聲明和函數聲明在一起時!!!

     
    

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章