JavaScript核心概念 - 語法作用域(Lexical Scope)

變數宣告與作用域

首先,我們要知道 JavaScript 的變數有它作用域的範圍,未使用 var 所宣告的變數都會自動變成全域變數,而切分變數有效範圍的最小單位是 “function” (ES6 之後有 let 與 const 分別定義「變數」與「常數」。 與 var 不同的是,它們的 scope 是透過大括號 { } 來切分的。)這邊我們舉一個例子,當我們將 console.log(ASin) 放在函式內執行函式時,其結果可以成功印出 ‘名字’,但若我們將其放在函式外時,其結果會顯示 ASin 這個變數並沒有被定義,這就是因為作用域的關係。

1
2
3
4
5
function callName() {
var ASin = '名字';
console.log(ASin);
}
callName(); // 名字
1
2
3
4
function callName() {
var ASin = '名字';
}
console.log(ASin); // Uncaught ReferenceError: ASin is not defined at <anonymous>

靜態作用域與動態作用域的差別

JavaScript 是採用靜態作用域(語法作用域),靜態作用域是原始碼在透過直譯器解析語法時,就確定了它的作用域,例如我們上面範例中 console.log(ASin) 在寫 function 時就已經定義好他的作用域。而動態作用域則是在函式調用的時候才決定它的作用域。

靜態作用域與動態作用域

JavaScript 作用域的查找

在我們所寫的原始碼中最外層會有一個全域的作用域,然後是各個 function 所形成的作用域,各 function 間的作用域是獨立的。但是,當某個 function 內需要取用一個變數,而這個 function 內沒有這個變數時,就會向外查找,若有則取用,若沒有則會回報錯誤。
作用域

靜態作用域與動態作用域執行的結果

所以當我們在寫好這些語法時,它的作用域就已經確定了,不會因為我們在執行時做改變。以下範例我們可以看到,ASin 的作用域在最外層,HYH 作用域只在 CallName2() 這個函式內,當我們執行 CallName2() 時,CallName1() 也會被調用並執行 console.log(name),其結果會是 ASin 而不是 HYH,這邊我們要記得我們在寫好語法時,它的作用域就已經確定了,所以 callName1() 內沒有 name 這個變數因而往外層查找,得到 ASin 這個結果。

那麼如果是動態作用域其情況就會不同,在調用CallName2()時,它會在函式宣告時才決定他的作用域,所以 callName1() 宣告時在 CallName2() 內,CallName1() 沒有 name 這個變數,所以往外層 CallName2() 查找,最後找到 HYH

1
2
3
4
5
6
7
8
9
var name = 'ASin';
function callName1() {
console.log(name);
}
function CallName2() {
var name = 'HYH'; //若為動態作用域時,callName1()取得 HYH
callName1();
}
CallName2(); // ASin
0%