上回與艾草玩遊戲輸了要接受處罰。
「都躲這麼遠了,她應該找不到我了吧!」
艾草:「啊哈,原來你躲在這裡呀!」
「你怎麼找到的?明明我跑超遠了呀。」
艾草:「範圍追蹤魔法呀,如果 1 km 內找不到你,那魔法就 5 km 、 10 km 的幫我找,就不信找不出你。」
「啊啊,這魔法也太好用了吧,快點教我吧!我一直在找尋很重要的東西。」
艾草:「可以呀!是要找什麼?」
「我遺失這些年的女朋友 (இ﹏இ 。)」
在開始範圍鍊 (Scope Chain)介紹之前,讓我們先了解作用域是什麼東西吧!
JavaScript 會在程式運行之前語法解析階段就決定作用域,稱為語法作用域(Lexical scope)。
在先前的文章-變數宣告 let
、const
、var
有提到。
首先,我們先來談談 ES6 才新增的宣告方式
let
、const
, 與var
不同的地方在let
、const
作用域在區塊,區塊指的是{}
內,例如if
、for
迴圈等,而var
的最小切分單位為function
。
作用域的劃分大致可區分為:
var
宣告的函式作用域
let
、const
宣告的塊狀作用域
以程式碼來舉例:
let myName = "艾草";
function sayHello() {
var myName = "烙詩";
if (true) {
let myName = "筑茵";
console.log(`塊狀作用域打招呼${myName}`);
}
console.log(`函式作用域打招呼${myName}`);
}
console.log(`全域打招呼${myName}`);
sayHello();
印出結果:
實際上的作用域劃分,會如下圖所示:
簡單來說 var
會被關在函式內, let
、const
會被關在 {}
大括號內,外層取用不到,而全域則是誰都可以取用。
外層作用域取不到內層作用域的值,但內層作用域如果找不到值,可以一直向外層作用域尋找,而這就是今天要介紹的範圍鍊 (Scope Chain)的觀念!
我們在撰寫 JavaScript 時,有時會宣告全域變數,而在函式作用域內也能去取用該全域變數,而範圍鍊便是指如果函式作用域內沒有找到指定變數時會向不斷向外尋找的過程,可以看到範例程式碼函式內並沒有宣告變數 a
、b
,卻能取用到:
let a = 1;
let b = 1;
function add() {
console.log(a + b);//2
}
add();
如果在函式內呼叫另一個函式,那它會取到哪個變數呢?
另外定義了一個函式 test
,並於 test
內分別也宣告了變數 a
、 b
,並於 test
內呼叫 add
函式,會發現印出來的結果依然是 2 ,所以函式 add
依然取用到全域的變數 。
let a = 1;
let b = 1;
function add() {
console.log(a + b);//2
}
function test() {
let a = 3;
let b = 4;
add();
}
test();
因為函式的區域內沒有該變數,所以它們會向全域尋找變數,為什麼在 test
函式內呼叫 add
函式並沒有影響到 add
函式印出的結果呢?因為 JavaScipt 的作用域在一開始就決定了,並不受執行堆疊與何時調用的影響。
如果直接將 add
函式移進 test
函式內結果會一樣嗎?
let a = 1;
let b = 1;
function test() {
let a = 3;
let b = 4;
function add() {
console.log(a + b);//7
}
add();
}
test();
會發現印出來的結果與上方不同了,因為巢狀函式內層的 add
函式在自己的作用域內找不到變數時,會往外層的 test
函式作用域尋找,而在 test
函式就能找到它需要的變數了,所以並不會繼續全域環境查找!
var
宣告的變數為函式作用域
let
、const
宣告的變數為塊狀作用域
請問以下 console.log()
會印出什麼呢?
let a = 1;
let b = 1;
function test() {
let a = 3;
let b = 4;
function add() {
let a = 2;
let b = 2;
console.log(a + b);//?
}
add();
}
test();
歡迎丟進開發人員工具檢視唷!
JavaScript 核心篇(六角學院)
https://codingbycolors.me/graphical_js_rules_scope/