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中變量的存儲原理,只不過這次我們重點要看的是對象!