JavaScript沒有塊級作用域經常會導緻了解上的困惑。在其他類C的語育中,由花括号封閉的代碼塊都有自己的作用域(如果用ECMAScript的話來講,就是它們自己的執行環境),因而支援根據條件來定義變量。例如,下面的代碼樣JavaScript中并不會得到想象中的結果:
if (true) {
var color = "blue";
}
alert(color); //*blue*
這裡是在一個if語句中定義了變量color。如果是在C、C++或Java中,color會在if語句執行完畢後被銷毀。但在JavaScript中,if語句中的變量聲明會将變量添加到目前的執行環境(在這裡是 全局環境)中。在使用for語句時尤其要牢記這一差異,例如:
for (var i=0; i < 10; i++){
doSomething(i);
}
alert(i); //10
對于有塊級作用域的語言來說,for語句初始化變量的表達式所定義的變量,隻會存在于循環的環境之中。而對于JavaScript來說,由 for 語句建立的變最 i 即使在for循環執行結束後,也依舊會存在于循環外部的執行環境中。
1. 聲明變量
使用var聲明的變量會自動被添加到最接近的環境中。在函數内部,最接近的環境就是函數的局部 環境;在with語句中,最接近的環境是函數環境。如果初始化變量時沒有使用var聲明,該變量會自 動被添加到全局環境。如下所示:
function add(num1, num2) {
var sum = num1 + num2;
return sum;
}
var result = add(10, 20); //30
alert(sum); //causes an error since sum is not a valid variable
以上代碼中的函數add()定義了一個名為sum的局部變量,該變鈕包含加法操作的結果。雖然結 果值從函數中傳回了,但變量sum在函數外部是通路不到的。如果省略這個例子中的 var 關鍵字,那 麼當add()執行完畢後,sum也将可以通路到:
function add(num1, num2) {
sum = num1 + num2;
return sum;
}
var result = add(10, 20); //30
alert(sum); //30
這個例子中的變量sum在被初始化指派時沒有使用var關鍵字。于是,當調用完add()之後,添加到全局環境中的變量sum将繼續存在;即使函數已經執行完畢,後面的代碼依舊可以通路它。
在編寫 JavaScript 代碼的過程中,不聲明而直接初始化變量是一個常見的錯誤做法,因為這樣可能會導緻意外。我們建議在初始化變量之前,一定要先聲明,這樣就可以避免類似問題,在嚴格模式下,初始化未經聲明的變量會導緻錯誤。
2.查詢辨別符
當在某個環境中為了讀取或寫入而引用一個辨別符時,必須通過搜尋來确定該辨別符實際代表什麼。搜尋過程從作用域鍊的前端開始,向上逐級查詢與給定名字比對的辨別符。如果在局部環境中找到了該辨別符,搜尋過程停止,變量就緒。如果在局部環境中沒有找到該變量名,則繼續沿作用域鍊向上搜尋。搜尋過程将一直追溯到全局環境的變址對象。如果在全局環境中也沒有找到這個辨別符,則意味着該變量尚未聲明。
通過下面這個示例,可以了解査詢辨別符的過程:
var color = "blue";
function getColor(){
return color;
}
alert(getColor()); //"blue"
調用本例中的函數 getColor() 時會引用變量color。為了确定變量color的值,将開始一個兩步的搜尋過程。首先,搜尋getColor() 的變量對象,查找其中是否包含一個名為color的辨別符。 在沒有找到的情況下,搜尋繼續到下一個變量對象(全局環境的變量對象),然後在那裡找到了名為 color的辨別符。因為搜尋到了定義這個變量的變量對象,搜尋過程宣告結束。
在這個搜尋過程中,如果存在一個局部的變量的定義,則搜尋會自動停止,不再進入另一個變量對象。換句話說,如果局部環境中存在着同名辨別符,就不會使用位于父環境中的辨別符,如下面的例子所示:
var color = "blue";
function getColor(){
var color = "red";
return color;
}
alert(getColor()); //"red"