作用域與詞法環境
這是一個非常核心的JavaScript概念。我們來詳細地解釋一下全局作用域、函數作用域和塊級作用域的區別。
核心概念:作用域
作用域 就是變量、函數和對象的可訪問性範圍。換句話説,它決定了代碼中不同部分的變量可見性。
1. 全局作用域
- 定義:在任何函數或代碼塊
{}之外定義的變量或函數,都擁有全局作用域。 - 生命週期:從定義開始,直到頁面關閉。
- 訪問性:在腳本中的任何地方(包括函數內部和代碼塊內部)都可以訪問和修改。
示例:
// 全局變量
var globalVar = "我是全局變量";
let globalLet = "我也是全局變量(但用let聲明)";
const globalConst = "我也是全局變量(但用const聲明)";
function myFunction() {
console.log(globalVar); // 在函數內部可以訪問
console.log(globalLet); // 可以訪問
console.log(globalConst); // 可以訪問
}
myFunction();
console.log(globalVar); // 在函數外部也可以訪問
注意:
- 在瀏覽器中,全局作用域是
window對象。因此,使用var聲明的全局變量會成為window對象的屬性(例如window.globalVar),但使用let和const聲明的則不會。
2. 函數作用域
- 定義:在函數內部聲明的變量擁有函數作用域。
- 生命週期:在函數被調用時創建,在函數執行完畢後銷燬。
- 訪問性:只能在定義它們的函數內部訪問。函數外部無法訪問。
示例:
function myFunction() {
// 函數作用域變量
var functionScopedVar = "我在函數內部";
let functionScopedLet = "我也在函數內部";
const functionScopedConst = "我同樣在函數內部";
console.log(functionScopedVar); // 可以訪問
console.log(functionScopedLet); // 可以訪問
console.log(functionScopedConst); // 可以訪問
}
myFunction();
// console.log(functionScopedVar); // 報錯:functionScopedVar is not defined
// console.log(functionScopedLet); // 報錯
// console.log(functionScopedConst); // 報錯
關鍵點:
- 無論使用
var、let還是const在函數內部聲明的變量,都具有函數作用域。
3. 塊級作用域
- 定義:由一對花括號
{}創建的代碼區域,例如if、for、while語句或單獨的一個{}。使用let和const聲明的變量僅限於其所在的塊內。 - 生命週期:在代碼塊被執行時創建,在代碼塊執行完畢後銷燬。
- 訪問性:只能在定義它們的代碼塊內部訪問。
示例:
if (true) {
// 這是一個塊
var varVariable = "我用var聲明";
let letVariable = "我用let聲明";
const constVariable = "我用const聲明";
console.log(varVariable); // 可以訪問
console.log(letVariable); // 可以訪問
console.log(constVariable); // 可以訪問
}
console.log(varVariable); // 可以訪問!因為var沒有塊級作用域
// console.log(letVariable); // 報錯:letVariable is not defined
// console.log(constVariable); // 報錯:constVariable is not defined
關鍵點:
var沒有塊級作用域,它會“穿透”if、for等塊級語句,直接成為其所在函數或全局作用域的變量。let和const擁有塊級作用域,這是 ES6 引入它們的重要原因之一。
對比表格
| 特性 | 全局作用域 | 函數作用域 | 塊級作用域 |
|---|---|---|---|
| 定義位置 | 所有函數和塊之外 | 函數內部 | {} 內部(如 if, for, while) |
| 關鍵字 | var, let, const |
var, let, const |
僅 let 和 const |
| 生命週期 | 頁面關閉時銷燬 | 函數執行完畢後銷燬 | 代碼塊執行完畢後銷燬 |
| 外部訪問 | 任何地方都可訪問 | 不可在函數外訪問 | 不可在塊外訪問 |
var 表現 |
成為全局變量,是window的屬性 |
函數內有效 | 無視塊,提升到函數/全局頂部 |
let/const表現 |
全局變量,但不是window的屬性 |
函數內有效 | 遵守塊,塊外無法訪問 |
重要補充:變量提升與暫時性死區
-
var的變量提升:
var聲明的變量會被提升到其作用域(函數或全局)的頂部,但只提升聲明,不提升賦值。在賦值前訪問會得到undefined。console.log(hoistedVar); // 輸出:undefined (而不是報錯) var hoistedVar = "我被提升了"; -
let和const的暫時性死區:
let和const雖然也有提升,但它們不會被初始化。從塊開始到聲明語句之間的區域稱為“暫時性死區”,在此區域訪問變量會拋出引用錯誤。console.log(deadZoneLet); // 報錯:Cannot access 'deadZoneLet' before initialization let deadZoneLet = "我在死區裏";
總結
- 全局作用域是最大的容器,隨處可訪問,但要避免污染全局命名空間。
- 函數作用域將變量隔離在函數內部,是組織代碼的基礎單元。
- 塊級作用域(由
let/const實現)提供了更精細的變量控制,尤其是在循環和條件語句中,避免了var帶來的一些怪異行為。
在現代JavaScript開發中,推薦始終使用 let 和 const 來聲明變量,以避免變量提升和塊級作用域相關的問題,使代碼更可預測和易於維護。