Hello!
上一篇關於JS中函式傳參(http://www.cnblogs.com/souvenir/p/4969092.html)的介紹中提到了JS的另外一個基本概念:JS變數儲存,
今天我們就用一個簡單的JS DEMO來開始介紹這個概念。
1 var a = 100; 2 3 function func(){ 4 console.log(a); 5 var a=200; 6 console.log(a); 7 } 8 9 func();
相信大家心裡面已經有了各自的答案。
....
來看下實際的執行結果:
納尼!怎麼會醬紫?我們明明定義了一個全域性變數a,按照JS作用域鏈的理論,func應該可以訪問到全部變數a的啊?
是的,按照作用域鏈的思想,func函式在執行時,在其區域性變數內找不到a變數的話,理應向上在全部作用局中繼續查詢。
問題就出在函式內部的 var a =200; 這句區域性變數定義。
我們都知道JS變數型別是鬆散型,鬆散型的意思並不是說JS變數就沒有變數型別,而是其變數型別是在執行時才進行確定。
來看一個DEMO:
1 var str; 2 3 str=2015;
第一行我們定義了一個變數str,但是並未賦值,這時候JS並不知道str變數的型別,等到指令碼執行到第3行,我們給str變數賦了一個值:2015.
這時候JS才知道,哦,原來str的值是2015,這不就是Number型別嗎,這才確定了str的型別。
這讓我想起了JS的另一個概念,叫作函式宣告提升!
把最開始的DEMO改造下:
1 var a = 100; 2 3 func(); 4 5 function func(){ 6 console.log(a); 7 var a=200; 8 console.log(a); 9 } 10
將函式的宣告放在了最後,但是程式碼仍然可以正常執行,並不會出現func未定義的錯誤。
函式宣告提升就說明JS在執行之前還會經歷另外一個過程:預載入。(有些地方也叫作預編譯)
在預載入階段,JS主要對全域性作用域、函式的執行環境以及作用域鏈等進行準備,
這裡的函式執行環境就是指:讀取變數定義並確定其屬於哪個作用域,但不會為其賦值!
到這裡我們終於要開始解釋文章一開始提出的問題了(可憋死寶寶了(づ。◕‿‿◕。)づ):
我們就來分解一下預載入階段,JS都做了什麼事情:
1 var a = 100; //定義一個全部變數 2 3 func(); 4 5 function func(){ 6 console.log(a); 7 var a=200; //定義一個func區域性變數 8 console.log(a); 9 }
在預載入階段,第一行的時候定義了一個全部變數a,然後到了第7行,又給func定義了一個區域性變數a,
注意這個時候變數並未賦值,值均為undefined
到了執行階段:
第一行給全部變數a賦了值:100,等到執行func函式的時候,
在第6行,需要使用a變數,JS當然是先查詢func的區域性變數了,沒錯,預載入階段已經為func定義了一個區域性變數a,
所以JS當然不會繼續往全域性進行查詢了,但是使用的時候才發現居然沒有值,也就是undefined!
等到第7行JS才給區域性變數a賦值。
小結:
JS分為預載入和執行期兩個階段,前者只會確定變數的作用域,在執行期才會對齊進行賦值,同時也就確定了變數的具體型別。
下一篇(http://www.cnblogs.com/souvenir/p/4969565.html)我們將繼續介紹JS中變數的儲存原理,只不過這次我們重點要看的是物件!