MDN上明確的說,JavaScript是一個輕量級的、解釋型的、面向對象的、將函數視爲一級公民的語言。
那麼,既然js是一個解釋型語言,那它就是在運行時把代碼轉換成機器能夠識別的語言。哪它又爲何會存在變量提高呢?
下面這篇文章給出瞭解釋,在此也特別感謝做者的分享!
https://mp.weixin.qq.com/s/ne...數組
function fn(c){ console.log(c); //true var c = false; console.log(a); //function a(){} var a = 1; console.log(a); //1 function a(){ }; console.log(a); //1 b(); //函數聲明 var b = function(){ console.log("函數表達式"); } function b(){ console.log( "函數聲明" ) } b(); //函數表達式 var d = 4; } fn(true);
上面這個fn方法很簡單,相信不少人都能過作出來,但確定也不少人不理解結果爲何是這樣!
那麼這個過程是怎樣執行的呢:
首先,js在執行前會產生一個GO(Global Object),也就是咱們常說的全局做用域。當一個方法被調用的時候會造成一個局部做用域AO(Activation Object)。
全局代碼在執行的時候,先是變量提高,在全局做用域內添加屬性,而後是函數(以函數聲明建立的函數)提高,再是代碼執行。
函數在被調用的時候,以上面的fn這個方法爲例,進入函數上下文,可是在執行代碼以前,經歷的階段:
第一階段:首先建立一個AO,幷包含下列屬性:閉包
AO : { arguments: <ArgO>, //函數內部參數的類數組對象 c: true, //傳入的參數,若是沒有傳遞實參,那麼值就是undefined }
第二階段:函數內部的函數(以函數聲明建立的函數)提高函數
AO : { arguments: <ArgO>, c: true, a: function a(){}, b: function b(){ console.log( "函數聲明" ) } }
第三階段:函數內部的變量提高code
AO : { arguments: <ArgO>, c: true, a: function a(){}, b: function b(){ console.log( "函數聲明" ) }, d: undefined }
函數內部又定義了變量 var c和var a,當變量提高時,發現AO中已經存在a和c屬性,若是變量名稱跟已經聲明的形參或函數相同,則變量聲明不會干擾已經存在的這類屬性。若是不相同切不存在,那就會放到AO中,值爲undefined。對象
當上面的這些準備工做作完以後,纔會開始執行函數內部的代碼,這樣就很好明白,函數內部的變量,在什麼階段分別表明什麼樣的值!ip
在JavaScript高級程序3中,對閉包的描述是這樣的,閉包是指有權訪問另外一個函數做用域中的變量的函數。那麼,很顯然,閉包其實就是一個函數。
那麼咱們怎樣經過GO、AO理解閉包呢:作用域
function foo(){ var a = 1; function fnSon(){ a++; console.log(a); } return fnSon; } var fnTest = foo(); fnTest(); //2 fnTest(); //3
當foo這個方法被調用的時候,建立一個AO,當函數執行完以後AO裏面包含了變量a、函數fnSonget
AO:{ a:1, fnSon: function(){} }
此時的fnSon被賦值給了fnTest,那麼foo方法的AO在執行完以後就沒辦法被銷燬,當fnTest被調用的時候,代碼執行完以後,foo的AO對象中的a變成了2,再次調用fnTest的時候就變成3。io