先來看個常見的面試題以下:面試
var a = 10; function test(){ alert(a); //undefined var a = 20; alert(a); //20 } test();
疑問:爲何呢?test()執行時,雖然a=20沒有賦值,可是父級做用域裏是有a=10的,不該該是undefined呀,js是按順序執行的,此時的var num = 20;根本沒有執行,因此應該是10!!你是否是也是這麼認爲的,就和我當初同樣???函數
分析:衆所周知,js代碼是自上而下執行的,JavaScript並非傳統的塊級做用域,而是函數做用域。JavaScript引擎會在代碼執行前進行詞法分析,因此事實上,js運行分爲此法分析和執行兩個階段。code
JavaScript代碼運行前有一個相似編譯的過程即詞法分析,詞法分析主要有三個步驟:對象
分析參數ip
再分析變量的聲明作用域
分析函數聲明io
具體步驟以下:編譯
函數在運行的瞬間,生成一個活動對象(Active Object),簡稱AOfunction
第一步:分析參數:test
函數接收形式參數,添加到AO的屬性,而且這個時候值爲undefined,即AO.name=undefined
接收實參,添加到AO的屬性,覆蓋以前的undefined
第二步:分析變量聲明:如var name;或var name='mary';
若是上一步分析參數中AO尚未name屬性,則添加AO屬性爲undefined,即AO.name=undefined
若是AO上面已經有name屬性了,則不做任何修改
第三步:分析函數的聲明:
若是有function name(){}把函數賦給AO.name ,覆蓋上一步分析的值
分析下面這個栗子:
1.var a = 10; 2.function test(a){ 3. alert(a); //function a (){} 4. var a = 20; 5. alert(a); //20 6. function a (){} 7. alert(a); //20 8. } 9. 10.test(100);
詞法分析:
第一步,分析函數參數:
形式參數:AO.a = undefined 接收實參:AO.a = 100
第二步,分析局部變量:
第4行代碼有var a,可是此時已有AO.a = 100,因此不作任何修改,即AO.a = 100
第三步,分析函數聲明:
第6行代碼有函數a,則將function a(){}賦給AO.a,即AO.a = function a(){}
執行代碼時:
第3行代碼運行時拿到的a時詞法分析後的AO.a,即AO.a = function a(){}; 第4行代碼:將20賦值給a,此時a=20; 第5行代碼運行時a已經被賦值爲20,結果20; 第6行代碼是一個函數表達式,因此不作任何操做; 第7行代碼運行時還是20;
ps:
1.var a = 10; 2.function test(a){ 3. var a; //證實詞法分析第二步。 4. alert(a); //100 5. a = 20; 6. alert(a); //20 7.} 7.test(100);
ps:
var a = 10; function test(a){ alert(a); //100 var a = 20; alert(a); //20 a = function(){} //是賦值,只有在執行時纔有效 alert(a); //function(){} } test(100);
ps:(執行結果同上)
var a = 10; function test(a){ alert(a); //100 var a = 20; alert(a); //20 var a = function(){} //是賦值,只有在執行時纔有效 alert(a); //function(){} } test(100);
補充說明:函數聲明與函數表達式
//函數聲明 function a(){ } //函數表達式 var b = function(){ }
a和b在詞法分析時,區別:
a在詞法分析時,就發揮做用; b只有在執行階段,才發揮做用。
詞法做用域
所謂詞法做用域是說,其做用域爲在定義時(詞法分析時)就肯定下來的,而並不是在執行時肯定。白話就是在函數未執行前,函數執行的順序已經被肯定,而不是相似JAVA同樣,是在執行前根本不知道執行順序。