作用域
通常來說,一段程序代碼中所用到的名字並不總是有效/可用的,而限定這個名字的可用性的代碼范圍就是這個名字的作用域.
作用域的使用提高了程序邏輯的局部性,增強程序的可靠性,減少名字沖突
詞法作用域
考慮如下情況:
var name = "Chromium";
function init() {
var name = "Mozilla"; // name 是一個被 init 創建的局部變量
function displayName() {
// displayName() 是內部函數,一個閉包
alert(name); // 使用了父函數中聲明的變量
}
displayName();
}
init(); // 彈出Mozilla
以及
var name = "Chromium";
function init() {
var name = "Mozilla"; // name 是一個被 init 創建的局部變量
function displayName() {
// displayName() 是內部函數,一個閉包
alert(name); // 使用了父函數中聲明的變量
}
return displayName;
}
init()(); // 彈出Mozilla
運行代碼可以發現 displayName() 內的 alert() 語句成功的顯示了在其父函數中聲明的 name 變量的值,js 引擎在預解析階段就已經把變量對象/作用域鏈接/this 值解析過了.詞法作用域中使用的域,是變量在代碼中聲明的位置所決定的,遇到既不是形參也不是函數內部定義的局部變量的變量時,去函數聲明時的作用域鏈查詢.嵌套的函數可以訪問在其外部聲明的變量.
我們可以看出這兩段代碼中 displayName 函數內部並沒有定義局部變量,函數內部訪問的變量是從作用域鏈上取值,作用域鏈是這樣的: global scope => init function scope => display function scope,從作用域鏈上取值最明顯的特點就是"就近原則",
Mozilla 離得近,所以取值就是 Mozilla
動態作用域
動態作用域不關心它本身是怎樣在哪里聲明的,只關心它在哪里調用的,動態作用域的域鏈基於調用棧,而不是代碼中的嵌套關系.動態域的函數中遇到既不是形參也不是函數內部定義的局部變量的變量時,到函數調用時的環境中查.
如果處於動態作用域中,下面代碼執行輸出就是 Chromium
var name = "Chromium";
function init() {
var name = "Mozilla"; // name 是一個被 init 創建的局部變量
function displayName() {
// displayName() 是內部函數,一個閉包
alert(name); // 使用了父函數中聲明的變量
}
return displayName;
}
init()(); // Chromium
首先 init()調用,獲取 displayName,然后執行 displayName(),這個時候 dispalyName 是在全局作用域中執行的,內部的 name 變量會在調用的環境中去找,全局作用域下的 name 就是 Chromium.
- 詞法作用域關心的是函數在哪里聲明的,動態作用域的概念和 js 中的 this 相同,this 也關心函數在哪里調用的.
- eval 和 with 可以產生動態作用域的效果
總結
- 詞法作用域:函數在定義的時候決定了函數的作用域,JavaScript 采用詞法作用域(靜態作用域).靜態作用域關心函數在何處被定義.
- 動態作用域:函數在調用的時候決定函數的作用域,目前只有部分語言支持.動態作用域關心函數在何處被調用.