理解javascript範圍和範圍鏈,javascript
範圍
範圍就是變數和函數的可存取範圍,控制著變數和函數的可見度與生命週期,在JavaScript中變數的範圍有全域範圍和局部範圍。
全域和局部範圍下面用一張圖來解釋:
單純的JavaScript範圍還是很好理解的。
範圍鏈
全域執行環境是最外層的一個執行環境,在web瀏覽器中全域執行環境是window對象,因此所有全域變數和函數都是作為window對象的屬性和放大建立的。每個函數都有自己的執行環境,當執行流進入一個函數的時候,函數的環境會被推入一個函數棧中,而在函數執行完畢後執行環境出棧並被銷毀,儲存在其中的所有變數和函數定義隨之銷毀,控制權返回到之前的執行環境中,全域的執行環境在應用程式退出(瀏覽器關閉)才會被銷毀。
當代碼在一個環境中執行時,會建立變數對象的一個範圍鏈(scope chain)來保證對執行環境有權訪問的變數和函數的有序訪問。
用一張圖來解釋範圍鏈的運行:由裡向外執行。
當一個函數建立後,它的範圍鏈會被建立此函數的範圍中可訪問的資料對象填充,例如定義下面這樣一個函數:
function add(num1,num2) { var sum = num1 + num2; return sum;}在函數add建立時,它的範圍鏈中會填入一個全域對象,該對象包含了所有全域變數,如所示:
函數add的範圍將會在執行時候用到,例如執行如下代碼:
function a(sum1,sum2){ var sum=num1+num2; return sum; } var tatal=a(5,10); 執行此函數時會建立一個稱為“運行期上下文(execution context)”的內部對象,運行期上下文定義了函數執行時的環境。每個運行期上下文都有自己的範圍鏈,用於標識符解析,當運行期上下文被建立時,而它的範圍鏈初始化為當前運行函數的[[Scope]]所包含的對象。
這些值按照它們出現在函數中的順序被複製到運行期內容相關的範圍鏈中。它們共同組成了一個新的對象,叫“使用中的物件(activation object)”,該對象包含了函數的所有局部變數、具名引數、參數集合以及this,然後此對象會被推入範圍鏈的前端,當運行期上下文被銷毀,使用中的物件也隨之銷毀。新的範圍鏈如所示:
在函數執行過程中,沒遇到一個變數,都會經曆一次標識符解析過程以決定從哪裡擷取和儲存資料。該過程從範圍鏈頭部,也就是從使用中的物件開始搜尋,尋找同名的標識符,如果找到了就使用這個標識符對應的變數,如果沒找到繼續搜尋範圍鏈中的下一個對象,如果搜尋完所有對象都未找到,則認為該標識符未定義。
總結
根據上述講的範圍鏈的結構可以看出,定義的標識符的越深,那麼讀寫的速度也就越慢,而全域變數總是處於範圍鏈的最末端,所以當變數解析的時候,尋找全域變數是最慢的,所以在編寫代碼的時候要儘可能少的使用全域變數,儘可能使用局部變數。
怎理解 javascript中的 範圍鏈?
範圍可能不是那麼難理解,關鍵是編碼規範。在第一個echo()中,由於在函數內定義了name,瀏覽器在執行時,也是先定義變數再使用,我認為第一個echo()瀏覽器解釋為如下代碼:
var name = 'laruence';
function echo() {
var name;
alert(name);
name = 'eve';
alert(name);
alert(age);
}
這樣第一個alert(name);就是undefined了。把函數內的var name去掉看,它就會顯示正常的值了。為避免這樣的問題發生,建議使用不同的名字,或者在函數內部開始時就聲明變數。我們應當按照適當的編碼規範去編寫代碼,而不是寫出難懂的或者模糊的代碼。
對於javascript中的範圍鏈中的問題?
這是一個變數範圍的問題,限於本人掌握的js理論,只能大概給你做個解釋。
js變數是先定義後訪問的原則,如果沒有定義就取值哪就變成了全域變數,
如:name="全域變數",定義的就是全域變數,相當於 window.name="全域變數"。也就是在全域的任何地方都可以訪問到這個變數。但在function中,function 相當於一個盒子,盒子裡的方法可以訪問外面的變數,但盒子本身聲明的變數卻是不能被外部存取的。這也就是大家掛在嘴邊的變數範圍的問題。不難理解
function(){
var name="局部變數"
}
alert(name) //undefined
因為name是定義在function 中的,是function私人的所以外部不能訪問。
但是為什麼會出現下面的結果呢
function(){
alert(name) //輸出為'undefined'
var name="局部變數" //這裡聲明了變數
}
首先是瀏覽器已經解析完了整個 function(){}即已經讀取完了整個function
上面已經說過了,js變數使用的方式是先定義後使用。因為function尋找變數是由內到外的機制,
即會先在自身內部尋找這個變數,是不是定義過的,如果不是再向外部尋找。
所以上面的代碼我們可以這樣理解:
當js解析到 alert(name) 時,因為function 已經知道了在其內部已經有這個name的變數了,所以name的變數不會向外部尋找,但又因為js解析又是由上而下的進行,即還沒解析到 var name="局部變數" ,所以這時的name是有名稱而沒有值的(有名無實)undefined。