變數的作用域(scope),指的是變數在指令碼程式碼中的可讀、寫的有效範圍,也就是指令碼程式碼中可以使用這個變數的區域。在 ECMAScript6 之前,變數的作用域主要分為全域性作用域、區域性作用域(也稱函數作用域)兩種;在 ECMAScript6 及其之後,
變數的作用域主要分為全域性作用域、區域性作用域和塊級作用域這 3 種。
相應作用域的變數分別稱為全域性變數、區域性變數和塊級變數。全域性變數宣告在所有函數之外;區域性變數是在函數體內宣告的變數或者是函數的命名引數;塊級變數是在塊中宣告的變數,只在塊中有效。變數的作用域跟宣告方式有很密切的關係。使用 var 宣告的變數的作用域有全域性作用域和函數作用域,沒有塊級作用域;使用 let 和 const 宣告的變數有全域性作用域、區域性作用域和塊級作用域。
注意:嚴格意義的全域性變數都屬於 window 物件的屬性,但 let 和 const 宣告的變數並不屬於 window 物件,所以它們並不是嚴格意義上的全域性變數,在此僅僅從它們的作用域這個角度來說它們是全域性變數的。
由於 var 支援變數提升,所以 var 變數的全域性作用域是對整個頁面的指令碼程式碼有效;而 let 和 const 不支援變數提升,所以 let 和 const 變數的全域性作用域指的是從宣告語句開始到整個頁面的指令碼程式碼結束之間的整個區域,而宣告語句之前的區域是沒有效的。
同樣,因為 var 支援變數提升,而 let 和 const 不支援變數提升,所以使用 var 宣告的區域性變數在整個函數中有效,而使用 let 和 const 宣告的區域性變數從宣告語句開始到函數結束之間的區域有效。需要注意的是,如果區域性變數和全域性變數同名,則在函數作用域中,區域性變數會履蓋全域性變數,即在函數體中起作用的是區域性變數;在函數體外,全域性變數起作用,區域性變數無效,此時參照區域性變數將出現語法錯誤。
對塊級變數來說,其作用域是塊級變數宣告語句開始到塊結束之間的區域。在塊開始到塊級變數宣告語句之間的區域為“暫時性死區”,在這個區域,塊級變數沒有效。
另外,在非嚴格執行模式中,變數可以不需要宣告,這些沒有宣告的變數,不管在哪裡使用都屬於全域性變數。通常不建議變數不宣告而直接使用,因為這樣有可能會產生一些不易發現的錯誤。
【例 1】變數的作用域範例。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>變數作用域範例</title>
<script>
var v1 = "JavaScript"; //全域性變數
let v2 = "JScript"; //全域性變數
let v3 = "Script"; //全域性變數
scopeTest(); //呼叫函數
function scopeTest(){
var lv = "aaa"; //區域性變數
var v1 = "bbb"; //區域性變數
let v2 = "ccc"; //區域性變數
if(true){
let lv = "123"; //塊級變數
console.log("塊內輸出的lv = " + lv); //123
}
console.log("函數體內輸出的lv = " + lv); //aaa
console.log("函數體內輸出的v1 = " + v1); //bbb
console.log("函數體內輸出的v2 = " + v2); //ccc
console.log("函數體內輸出的v3 = " + v3); //Script
//v4為全域性變數,賦值在後面,因而值為undefined
console.log("函數體內輸出的v4 = " + v4);
}
var v4 = "VBScript"; //全域性變數
console.log("函數體外輸出的lv = " + lv); //① 報ReferenceError錯誤
console.log("函數體外輸出的v1 = " + v1); //JavaScript
console.log("函數體外輸出的v2 = " + v2); //JScript
console.log("函數體外輸出的v3 = " + v3); //Script
console.log("函數體外輸出的v3 = " + v4); //VBScript
</script>
</head>
<body>
</body>
</html>
上述指令碼程式碼分別宣告了 4 個全域性變數、3 個區域性變數和 1 個塊級變數。在 scopeTest 函數體外,變數 v1、v2、v3 和 v4 為全域性變數;在 scopeTest 函數體內,lv、v2是全域性變數;在 if 判斷塊中,lv 是塊級變數。
我們看到,區域性變數 v1 和 v2 與全域性變數 v1 和 v2 同名,在 scopeTest 函數體內,區域性變數 v1 和 v2 有效,因而在函數體這 2 個變數的輸出結果分別為“bbb”和“ccc”;在函數體外,全域性變數 v1 和 v2 有效,因而在函數體外,這 2 個變數的輸出結果分別為“JavaScript”和“JScript”。
另外,塊級變數 lv 和區域性變數 lv 同名,在 if 判斷塊中,塊級變數 lv 有效,因而在塊中輸出的結果為“123”,而在塊外,區域性變數 lv 有效,lv 變數的輸出結果為“aaa”。
另外,全域性變數 v3 和 v4 在函數體中沒有被覆蓋,因而輸出的是全域性變數的值,所以 v3 在函數體內和體外的輸出結果都為“Script”,而 v4 變數的賦值在函數呼叫的後面,因而在函數體中的 v4 輸出結果為“undefined”,而在函數體外的輸出是在宣告之後,所以結果為“VBScript”。lv 是區域性變數,因而在函數體外存取會報“ReferenceError”錯誤。
上述程式碼在 Chrome 瀏覽器中執行後,開啟瀏覽器的控制台,可以看到圖 1 所示的輸出結果。
圖1:① 處程式碼註釋前控制台輸出結果