0%

Javascript Function 作用域!一起用小明開分身的故事學習作用域!


當我們學會了 Javascript 的 Function 怎麼使用後,有沒有常常因爲參數、變數、全域變數傻傻分不清楚的問題呢?今天我們就來聊聊 Javascript 的 Function Scope 吧!


HIHI~😍 如果你是第一次來的話,『Chan-Chan-Dev』是一個專門用簡單的圖文與故事講解網路程式技術的網站。
若你也喜歡用這種方式學習的話,歡迎加入 Chan-Chan-Dev Facebook 的粉絲團,在發佈的時候就有比較多機會收到通知喔!😍

Function 前情提要

在上一篇 Javascript Function 超基礎入門!老司機勿入! 提到 Function 的最基礎用法,若錯過的朋友可以先花個簡單的幾分鐘瀏覽一下喔 😀

Function Scope 函數作用域

什麼是 Function Scope 函數作用域呢?

在回答這個問題之前,讓我們一樣來個小故事吧 😆

從前從前從前在一塊元古的大陸上,這塊大陸上有著好幾個大大小小的原始的部落。

原始的部落想像圖 (請原諒我沒有精稿 XDD)

這些部落裡中有個不成文且流傳已久的奇怪規定,就是『任何一個從這個部落出生的人,終其一生都不可以跨越邊界』,因此每個部落的首領下令用粗壯的樹幹圍出部落的邊界,並且禁止部落的人到外頭去。

首領:「任何一個從這個部落出生的人,終其一生都不可以跨越邊界!」

小明是出生在部落 A的小孩,因此你在部落 A呼喊『小明』的名字,你就可以找到部落 A的小明。

他是部落 A 小明

相反地,如果你在部落 A以外的地方呼喚『小明』的名字,部落 A以外的人就會對你黑人問號,不知道你在找誰,跟你說這裏沒有這個人啦 😵

我們把鏡頭還給棚內 !


我們可以把整個 <script>....</script> 之間的區域假想爲一塊大陸,在實際宣告 function 的時候也會因爲那一對 {} 圍出一個 區域 ,而這個區域就叫做 Scope 作用域。就像是圍出一個個不同的部落

function 所圍出的邊界就像是 電網 一樣,因此若是在這個區域裏面出生的孩子(例如:宣告的變數、參數)都被告知絕對不可以走出這個區域以外 的地方 😭

也因為他們只能活在這對{} 所形成的區域內,因此被稱爲 區域變數 Local Variables

簡單地來看一下這段程式範例:

1
2
3
4
5
6
function a() {
const min = "部落 A 小明";
console.log(min);
}
a();
console.log(min);

在執行過後,就會發現 Console 印出:

部落 A 小明 👈 由第 3 行印出
Uncaught ReferenceError: min is not defined 👈 執行第 6 行發生錯誤

在第二行的 min 是在 a function 裏面宣告的,因此它的 scope (作用域 / 生存範圍) 就只能在第 2 ~ 3 之間。

也因為如此,當我們在 a function {} 以外的範圍,試圖地取用 min 這個變數時,就會像是在部落 A外頭呼叫小明的名字一樣,因為沒有人認識,所以就會得到 min is not defined 的錯誤囉 😆

避免命名衝突

另一個 function 的區域變數同名的狀況

就是這麼巧,因為小明是一個菜市場名!

他是部落 B 小明

在另外一個部落 B剛好也有一個小明,如果在部落 A裡呼喚小明的話,你只會找到部落 A裡的小明,你不會找到部落 B的小明,因為部落 B的小明根本不在部落 A阿!

反之,在部落 B的話,是只會找到部落 B的小明的,不會找錯找到部落 A的小明。

1
2
3
4
5
6
7
8
9
10
11
12
function a(){
const min = "部落 A 小明";
console.log(min); // 👈 部落 A 小明
}

function b(){
const min = "部落 B 小明";
console.log(min); // 👈 部落 B 小明
}

a();
b();

因此,function 有像是部落的邊界作爲隔絕的話,就可以像上述一樣避免命名衝突的問題了。
我們可以看到 ab 有一樣命名的變數 min,但是因爲是在不同的 function 內存取,因此就不會有互相撞名衝突的問題囉!

function 外的同名變數狀況

小明因爲太菜市場名了!出現了一個出生在大陸上的小明,他不屬於任何部落,就自己漂流在這片大陸上,那。。那會發生什麼事情呢?
他是漂流大陸的小明

1
2
3
<script>
const min = "漂流大陸的小明";
</script>

相較於宣告在 function 的區域變數,若他不是宣告在 function 內的話,則會稱作全域變數,就像是上面宣告在最外層的 min 變數一樣。

如果我們一樣在部落 A的呼喊『小明』的名字,因爲部落 A的人只認識部落 A的小明,因此你還是可以找到部落 A的小明。

1
2
3
4
5
6
7
const min = "漂流大陸的小明";

function a(){
const min = "部落 A 小明";
console.log(min); // 👈 "部落 A 小明"
}
a();

如果這時候你在部落 A以外的地方呼喊『小明』的名字,大陸上的居民只認識漂流大陸的小明,因此你就可以找到漂流大陸的小明,你不會找到任何一個部落裡的小明。

1
2
3
4
5
6
7
8
const min = "漂流大陸的小明";

function a(){
const min = "部落 A 小明";
console.log(min); // 👈 "部落 A 小明"
}

console.log(min); // 👈 "漂流大陸的小明"

如果部落裡沒有這個人…

如果今天後山的部落 C 根本沒有小明這個人,但是我們卻在部落 C呼喊『小明』的名字,我們會找的到小明嗎?就算有找到的話會找到哪一個小明呢?

尋人的步驟是這個樣子的:

部落裡有你要找的人,你就可以找到的到這個人。

但如果發現沒有你要找的這個人,就會派渡鴉到部落以外一層去尋找你要找的人囉。

因爲部落 C 建立在大陸上,因此你在部落 C呼喊『小明』的名字,部落沒有小明這個人的話,就會往外找到漂流大陸的小明囉!

1
2
3
4
5
6
7
const min = "漂流大陸的小明";

function c(){
console.log(min); // 👈 "漂流大陸的小明"
}

c();

巢狀 function 狀況

我們把上述的程式碼稍微調整一下,改爲以下:

1
2
3
4
5
6
7
8
9
function a(){
const min = "部落 A 小明";
function c(){
console.log(min);
}
c();
}

a();

若把 c function 改放進 a function 內的話,這時候的 min 變數會是誰呢?

還記得這個尋人邏輯嗎?

部落裡有你要找的人,你就可以找到的到這個人。

但如果發現沒有你要找的這個人,就會派渡鴉到部落以外一層去尋找你要找的人囉。

可以稍微用上面的尋人邏輯去想想看答案喔 😀

到底是哪個小明呢!?

“部落 A 小明”! 因爲 a function 爲 c function 的外層,因此若往外找到 a function 內有 min 這個變數就會直接使用囉! 相信聰明的你一定猜對了吧!

參數

我們在 Javascript Function 超基礎入門!老司機勿入! 有提到 參數 是用來作爲 function 輸入

那他究竟是區域變數還是全域變數呢?

執行底下程式碼會得到 min is not defined 的錯誤!

1
2
3
4
function a(min){
// ....
}
console.log(min);

區域變數”! 因此他 Scope 作用域也只會作用在在這個 function 內喔!

我們知道參數區域變數後,我們也一起來猜猜底下第 5 行的 min 變數究竟是誰吧 😆

1
2
3
4
5
6
7
8

const min = "漂流大陸的小明";

function a(min){
console.log(min);
}

a("部落 A 小明");

到底是哪個小明 ?

答案是:”部落 A 小明”! 因爲 a function 沒有 min 這個區域變數,因此若有參數的情況下,會找找看是否有這個 min 的參數,若有找到就會直接使用囉! 相信聰明的你一定猜對了吧!

Block Scope 區塊作用域

咦?那例如 if 或者是 for 等等的語法也有一對 {},這樣也會有 Scope 作用域 嗎?

沒錯!他們也是 Scope 作用域 的一種,叫做 區塊作用域 Block Scope

不過前提是變數的宣告是要使用 ES6 的 letconst 方式喔!若是使用 var 的方式除了 function 以外的 {} 就沒有作用域可言了 😭

我們稍微拿 if 來比較一下吧

1
2
3
4
5
6
7
8
9
if(true){
var min1 = "var 小明";
const min2 = "const 小明";
let min3 = "let 小明";
}

console.log(min1);
console.log(min2);
console.log(min3);

執行後會發現 min1 居然在第 7 行,也就是 if 之外居然還可以被存取到!

剩下的 min2min3 因爲無法在 if 的作用域之外被存取,而會產生 min2 is not defined 的錯誤囉!

結論

爲了講個 Scope 居然得畫這麼多個小明加上場景(而且畫風還有點不一樣 😆)

在這篇文章我們講解了 Javascript 的 function 作用域是什麼,以及為什麼需要他。

最常遇到的是 function 變數撞名衝突後,因爲對於 function 作用域的沒有完全地理解而造成預期以外的 Bug。

希望這篇文章用一個瞎掰的小故事可以幫助到大家對於 function 作用域更瞭解一些。


最後感謝你願意看到這裏,希望這篇文章對你有所幫助。

若你想到身邊有需要這篇文章內容的朋友,也請你幫他一個忙把這篇文章分享給他 😍

若文章的內容有錯誤的地方,也歡迎隨時一起討論交流。😘

最後感謝你的閱讀囉,我們下次見!Bye ~ 🚀

參考