JavaScript 是採用「語法作用域」
- 「lexical」的意思是「詞的,辭彙的」(adjective)
範例
先定義一個 function 叫做「callName」
1 | function callName() { |
執行函式callName()
,就會在 Console 印出console.log(Ming)
但是,如果我把console.log(Ming)
移出「callName 函式」外
1 | function callName() { |
在 Console 就會出現錯誤:「Ming is not defined」
原因為:
‼️ JavaScript 的作用域是在「函式裡面」,因此,在「函式裡面」宣告的變數,在外面是讀不到的
JavaScript 的「作用域」是「一層一層向內的」
- 最外層:會有一個「全域」的作用域
- 內層:有一個一個的 function 包著,各自的作用域是獨立的
- 以下面的例子,有「
callMe
、fn2
」這兩個 function,兩個 function 的作用域是「各自獨立」的
- 以下面的例子,有「
如果在 function 的作用域內需要某個變數,但是在 function 的作用域內沒有這個變數時,就會「往外層查找」
- 如果外層有這個變數,就會直接取來使用
- 如果外層也找不到這個變數,就會出現錯誤:「Uncaught ReferenceError: xxx is not defined」
語法作用域
靜態作用域(又稱為“語法作用域”)
🎃 靜態作用域,又稱為「語法作用域」–> 變數的作用域在語法解析時,就已經確定作用域,且作用域不會再改變
-
JavaScript 是採用「語法作用域」,跟「JavaScript 是如何運行的」有很大關係
-
JavaScript 是屬於「直譯式語言」–> 會直接透過「直譯器」解析原始碼之後生成代碼,接著就可以直接運行代碼
也就是說,當我在宣告 function 時,變數的作用域就已經確定了
語法作用域的範例
在下面的程式碼中,JavaScript 的執行順序是這樣的:
- 在外層先宣告一個變數–>
var value = 1;
- 執行「fn2 函式」–>
fn2();
- 在執行「fn2 函式」時,先重新宣告「
value
變數 = 2」–>var value = 2;
- 接著,再執行
fn1();
,會印出console.log(value);
這時,value
變數的值會是多少呢?
因為 JavaScript 是屬於「語法作用域」,因此,在我撰寫 function 時,作用域就已經確定了
答案是:在「靜態作用域」,console.log(value)
的值會等於「1」
1 | var value = 1; |
📍 首先,在外層宣告的變數var value = 1;
,它的作用域是「全域」:
- 因此,在「fn1 函式、fn2 函式」內,都可以讀取到
value
的值 = 1
📍 雖然在「fn2 函式」內,將「變數value
」的值重新宣告為 2,但是var value = 2;
的作用域就只限制在「fn2 函式」內
📍 因此,當我在「fn1 函式」執行console.log(value);
時,就會「往外層查找變數value
」–> 外層的value
= 1
動態作用域
🎃 「動態作用域」–> 變數的作用域在「函式調用」時才決定
動態作用域的範例
在下面這段程式碼中,JavaScript 執行的順序是這樣的:
- 在全域宣告變數
var value = 1;
- 執行「fn2 函式」時,重新定義
value = 2
,接著會執行「fn1 函式」 - 在執行「fn1 函式」時,會去查找這個
value
的值:
因為「動態作用域」是在「函式調用」時,才會決定作用域
因此,「fn1 函式」會往「fn2 函式(也就是調用 fn1 函式 的地方)」查找value
的值–> 在「fn2 函式」內的value
= 2
1 | var value = 1; |
因此,如果是動態作用域,最後value
的值會是「2」