估計大家一直對Js的作用域有點迷糊,今天沒事看到JavaScript權威指南對作用域的解釋感覺很不錯就和大家分享一下。
一:函數作用域
先看一小段代碼:
1. var scope="global";
2. function t(){
3. console.log(scope);
4. var scope="local"
5. console.log(scope);
6. }
7. t();
第一句輸出的是: "undefined",而不是 "global"
第二句輸出的是:"local"
你可能會認爲第一句會輸出:"global",因爲代碼還沒執行var scope="local",所以肯定會輸出“global"。
在C/C++中,花括號內中的每一段代碼都具有各自的作用域,而且變量在聲明它們的代碼段之外是不可見的,我們稱爲塊級作用域。而Javascript壓根沒有塊級作用域,而是函數作用域.
所謂函數作用域就是說:變量在聲明它們的函數體以及這個函數體嵌套的任意函數體內都是有定義的。
其實由於函數作用域的特性,局部變量在整個函數體內始終是有定義的,只有在程序執行到var語句的時候局部變量才真正的被賦值,上面的過程據就是我們經常說的“聲明提前”,將函數體內的變量聲明提前至函數的頂部,同時變量的初始化留在原地。
所以根據函數作用域的意思,可以將上述代碼等價於:
1. var scope="global";
2. function t(){
3. var scope;
4. console.log(scope);
5. scope="local"
6. console.log(scope);
7. }
8. t();
我們可以看到,由於函數作用域的特性,局部變量在整個函數體始終是由定義的,我們可以將變量聲明”提前“到函數體頂部,同時變量初始化還在原來位置。
1. var name="global";
2. if(true){
3. var name="local";
4. console.log(name)
5. }
6. console.log(name);
都輸出是“local",如果有塊級作用域,明顯if語句將創建局部變量name,並不會修改全局name,可是沒有這樣,所以Js沒有塊級作用域。
現在很好理解爲什麼會得出那樣的結果了。scope聲明覆蓋了全局的scope,但是還沒有賦值,所以輸出:”undefined“。
所以下面的代碼也就很好理解了。
1. function t(flag){
2. if(flag){
3. var s="ifscope";
4. for(var i=0;i<2;i++)
5. ;
6. }
7. console.log(i);
8. console.log(s);
9. }
10. t(true);
輸出:2 ”ifscope"
二:作用域鏈
先來看一段代碼:
1. name="lwy";
2. function t(){
3. var name="tlwy";
4. function s(){
5. var name="slwy";
6. console.log(name);
7. }
8. function ss(){
9. console.log(name);
10. }
11. s();
12. ss();
13. }
14. t();
當執行s時,將創建函數s的執行環境(調用對象),並將該對象置於鏈表開頭,然後將函數t的調用對象鏈接在之後,最後是全局對象。然後從鏈表開頭尋找變量name,很明顯
name是"slwy"。
但執行ss()時,作用域鏈是: ss()->t()->window,所以name是”tlwy"
Javascript權威指南寫的囉嗦,我們總結一下就是,在函數作用域內自上而下引用形成的鏈式結構就叫做作用域鏈