在實際開發(fā)中甥材,作用域對于任何語言而言都是一個不可忽視的極為重要的知識點顷窒。今天就來談談JavaScript的作用域和執(zhí)行環(huán)境。
首先作谭,看一下幾個重要概念稽物。
- 執(zhí)行環(huán)境:
執(zhí)行環(huán)境(execution context,為簡單起見折欠,有時也稱為“環(huán)境”)是JavaScript 中最為重要的一個概念贝或。執(zhí)行環(huán)境定義了變量或函數有權訪問的其他數據吼过,決定了它們各自的行為∵浣保——《JavaScript高級程序設計(第3版)》
每個函數都有一個自己的執(zhí)行環(huán)境盗忱。
某個執(zhí)行環(huán)境中的所有代碼執(zhí)行完畢后,該環(huán)境被銷毀羊赵,保存在其中的所有變量和函數定義也隨之銷毀(全局執(zhí)行環(huán)境直到應用程序退出——例如關閉網頁或瀏覽器——時才會被銷毀)趟佃。——《J3》
- 變量對象:
每個執(zhí)行環(huán)境都有一個與之關聯(lián)的變量對象(variable object)昧捷,環(huán)境中定義的所有變量和函數都保存在這個對象中闲昭。雖然我們編寫的代碼無法訪問這個對象,但解析器在處理數據時會在后臺使用它靡挥⌒蚓兀——《J3》
作用域鏈:
當代碼進入到某個執(zhí)行環(huán)境,準備執(zhí)行時跋破,會為該執(zhí)行環(huán)境對應的變量對象創(chuàng)建一個作用域鏈簸淀。作用域鏈其實就相當于一個變量對象的集合,其第一個元素是當前執(zhí)行環(huán)境的變量對象毒返,最后一個元素是全局執(zhí)行環(huán)境的變量對象(在瀏覽器中即window
對象)租幕。標識符解析:
標識符解析是沿著作用域鏈一級一級地搜索標識符的過程∨◆ぃ——《J3》
標識符解析說白了就是查找變量(包括函數定義)令蛉。而且這個查找過程是按照作用域鏈的順序走的,也就是先搜索當前執(zhí)行環(huán)境的變量對象狡恬,找到就終止珠叔,沒找到就繼續(xù)搜索上一層執(zhí)行環(huán)境的變量對象,一直搜索到頂層的window
對象弟劲。
純文字的表述不容易理解祷安,還是上代碼吧:
<script>
var common = 'Hello';
function sayHello() {
var say = 'Chuck';
say = common + say; //在sayHello()函數這個執(zhí)行環(huán)境內調用common變量
alert(say);
}
sayHello(); //'HelloChuck'
</script>```
這段代碼之所以能在`sayHello()`這個函數中調用變量`common`成功,就是因為可以在函數`sayHello()`的執(zhí)行環(huán)境的變量對象的作用域鏈上找到變量`common`兔乞。分析一下可以知道汇鞭,這個作用域鏈包含2個變量對象,第一個就是`sayHello()`函數的變量對象庸追,里邊只有一個局部變量`say`霍骄,這里找不到`common`,然后在第二個變量對象——`window`對象中找到了`common`淡溯。這個過程就是依靠于作用域鏈的標識符解析读整。
因此也可以理解,作用域鏈是一個線性的咱娶、單向的米间、層級的搜索機制强品。JS中所有變量或函數的訪問或調用,都遵循這一機制屈糊。
***
上面說的是JavaScript的執(zhí)行環(huán)境和作用域鏈的榛,下邊再來看一下JavaScript于其他語言的另一個不同點:塊級作用域。
直接上代碼:
```javascript
<script>
if (true) {
var color = "blue";
}
alert(color); //"blue"
</script>
這段代碼在js中沒有任何問題逻锐,但是對其他大部分語言而言夫晌,這段代碼并不能輸出 "blue"
而是會報錯,原因在于在其他語言中昧诱,由花括號 {}
封閉的 if
代碼塊是一個塊級作用域慷丽,在這個塊級作用域中聲明的局部變量會在此部分代碼執(zhí)行完畢后立即銷毀,因而外部無法再獲取到這些局部變量鳄哭。但是在 js 中,并沒有塊級作用域的概念(ES6中新增了塊級作用域的聲明及相關關鍵字纲熏,但在ES6得到規(guī)范性的支持和應用之前妆丘,我們這里都暫以ES5為準),其所有變量和函數的訪問和調用都是采用作用域鏈的機制執(zhí)行搜索的局劲。上面這段代碼屬于同一個執(zhí)行環(huán)境勺拣,在 if
中聲明的變量會被添加到當前執(zhí)行環(huán)境(在這里是全局環(huán)境)的變量對象中,直到當前執(zhí)行環(huán)境所有的代碼都執(zhí)行完畢后鱼填,當前執(zhí)行環(huán)境才會被銷毀药有,其中的變量和函數也隨之銷毀(即執(zhí)行環(huán)境的變量對象被銷毀)。因此苹丸,這段代碼在 js 中可正常輸出 "blue"
愤惰,因為 if 判斷執(zhí)行完之后變量 color
并沒有被銷毀。
因此赘理,也可以理解宦言,js 中 for
循環(huán)的循環(huán)變量 i
在循環(huán)結束后仍然存在于循環(huán)外部的當前執(zhí)行環(huán)境中,而不是像其他語言那樣商模, for
循環(huán)執(zhí)行完畢后 i
立即被釋放而無法在該循環(huán)外部訪問奠旺。