JavaScript Weird Part (10) - 範圍鏈 Scope

當我們需要使用變數時,JavsScript 不只會在目前的執行環境的變數環境尋找。因為每個執行環境都會創造一個特殊的變數叫做 this 以及外部環境的參照。每個執行環境都有一個參照到他的外部環境。

在函式 b 的狀況,它的外部環境是全域執行環境,a 函式也是同樣狀況。即使 a 在執行堆中被壓在 b 下面,重點在於參照的究竟是哪種外部環境?

1
2
3
4
5
6
7
8
9
10
11
function b() {
console.log(myVar);
}

function a() {
var myVar = 2;
b();
}

var myVar = 1;
a();

這就必須回到詞彙環境,也就是程式碼實際被寫出來的位置,決定 Javascript 引擎會如何處理程式的所在位置、記憶體中的位置以及和其他程式的連結。所以函式 b 在詞彙上,在全域環境上,表示它不在 a 函式裡,它和最後一行的 var myVar=1 同等級。

執行的順序會決定它們如何被呼叫及這些執行環境如何被創造,然而 Javascript 注重詞彙環境,當它面對每個執行環境都有的外部環境,如果需要某個執行環境裡的變數,如果它無法在那個執行環境裡面找到變數,它會到外部環境中尋找變數。而它指向的外部環境,會因為函式實際上的位置而有所不同。

所以 b 函式不在 a 函式裡,而是在最外層全域等級的地方,所以他的外部詞彙環境是全域的,對 a 函數來說也是。要執行函數 b 裡的 myVar 時找不到變數,所以只好繼續在全域環境尋找。

範圍鏈

這些執行環境被創造,和程式碼在哪裡被寫出來無關,它和執行環境何時被創造及何時被呼叫有關。在執行函數的時候 Javascript 為執行環境創造外部環境,並且基於函數在 Javascript 中的位置創造適當的外部參照。這就是搜尋範圍鏈到外部環境的過程。「範圍」的意思就是我能夠取用這個變數的地方,「鏈」則是外部環境參照的連結。所以函式 b 裡找不到變數,它會一路從範圍鏈往下找。

在以下範例中,因為在創造階段,全域執行環境沒有找到 b 函式,只有 a 函式,

1
2
3
4
5
6
7
8
9
10
11
function a() {
function b() {
console.log(myVar);
}

b();
}

var myVar = 1;
a();
b();

所以呼叫 b,會找會得出一個找不到的參照:

但是如果呼叫 a

1
2
3
4
5
6
7
8
9
10
11
function a() {
function b() {
console.log(myVar);
}

var myVar = 2;
b();
}

var myVar = 1;
a();

因為 b 的物理位置在 a 裡面,Javascript 會認定它的外部參照是 a,因此當函式 b 要取用 myVar 時,它在函式 b 找不到,它會在 a 找到 myVar,而不是全域環境,所以得出 2

為了理解範圍鏈的尋找模式,物理位置可以理解成詞彙位置,所以以下範例的結果結果是 1,因為在 因為 b 的物理位置在 a 裡面,Javascript 會認定它的外部參照是 a,因此當函式 b 要取用 myVar 時,它在函式 b 找不到,它在外部參照環境 a 也找不到 myVar,直到全域執行環境才找到。

1
2
3
4
5
6
7
8
9
10
function a() {
function b() {
console.log(myVar);
}

b();
}

var myVar = 1;
a();

範圍鏈的方向,端看當下的外部參照而決定。如果不想用詞彙環境來思考的話,可以想想是誰創造的?外部參照會找到被函數創造的執行環境,在上面的例子 a 函數的執行環境先被創造,才出現 b 函數。所以函數的位置,決定它的外部環境。

Powered by Hexo and Hexo-theme-hiker

Copyright © 2013 - 2020 CYC'S BLOG All Rights Reserved.

UV : | PV :