在學習 JavaScript 的過程中,變數(Variable) 是你最常使用也最基礎的概念之一。
每當你要儲存資料、處理邏輯、或撰寫函式時,變數就會登場。
而在使用變數時,有一個非常重要但常被忽略的核心概念,那就是:變數的作用範圍(Scope)。
簡單來說,作用範圍就是「某個變數在程式的哪些地方能夠被讀取或使用」。
你可以把作用範圍想像成通行證制度:變數就像一個地區限定的票證或門票,只能在特定區域內使用。
例如,一張捷運票只能在進站後的車站區域內有效,離開閘門後就不能再用了。
同樣地,在程式中,變數也只能在它被「發行」的範圍內被讀取或操作,離開那個範圍,它就不再有效了。
什麼是作用範圍(Scope)?
在 JavaScript 中,當你宣告一個變數時,這個變數不會對整個程式「公開」。
它只能在某些地方被「看到」或「使用」,這個可見的範圍就叫做「作用範圍(scope)」。
你可以把它想像成變數有一張通行證,只能在特定的區域內通行。
離開那個範圍,這張票就無效了。
這樣的設計可以讓程式更有結構,變數之間不容易「打架」,也能避免無意間改錯值。
但為什麼需要這樣的設計呢?答案其實很簡單 —— 如果所有變數都能到處用,程式就會變得非常難懂,而且很容易出錯。
我們用一個購物網站的例子來說明。
想像你正在開發一個購物網站,程式裡有好幾個功能:計算購物車總金額、產生營收報表、處理結帳付款。
這些功能都需要用到變數,而「作用範圍」就是讓這些變數各自安好、不互相搞亂的關鍵。
避免同名變數互相干擾
在這個購物網站中,計算購物車總金額的程式碼用了一個變數叫 total,用來加總使用者選的商品價格。
同時,另一段負責營收報表的程式碼,也用了一個叫 total 的變數,來記錄整個月的總營收。
如果這兩個 total 沒有各自的範圍限制,而是放在同一個共用的範圍中,那麼它們就會互相影響。
可能你在處理購物車時改了 total,結果無意間也把報表的數值改掉,讓資料顯示錯誤,甚至導致 bug 難以追查。
有了作用範圍,這兩個 total 就可以各自存在於不同的區塊中,互不干擾。你可以自由地使用相同的變數名稱,也不用擔心一邊的邏輯會破壞到另一邊的資料。
降低變數除錯的難度
接下來,假設你在測試時發現購物車的 total 金額算出來是錯的。
你想找出是哪裡出了問題,但如果 JavaScript 沒有作用範圍,total 可以在整份程式的任何地方被宣告、改值或覆蓋。
這代表你必須把購物車、報表、結帳、甚至不相關的功能全部翻過一遍,才有可能查出是誰改了 total。
當程式碼只有幾十行時還能接受,但如果你的購物網站有幾千行程式碼,找一個變數被誰改過就像在大海撈針一樣痛苦。
有了作用範圍後,total 的活動空間就被限制住了。
如果 total 是在計算購物車的函式裡宣告的,那你就可以放心地知道:這個變數只存在這個函式裡,只有這裡面的程式碼會動到它。
這樣不但能更快鎖定問題的位置,也能大幅減少除錯的時間。
限制變數範圍,避免資料外洩
最後,當使用者要結帳時,程式需要處理信用卡卡號。
你會在結帳函式裡用一個變數 cardNumber 來暫存這筆資料:
function checkout(cardNumber) {
// 結帳與付款邏輯
}這個 cardNumber 是你只想在結帳流程中使用的變數,如果沒有作用範圍的限制,它就有可能「洩漏」到其他地方被看見或誤用。
舉例來說,其他功能的程式碼不小心讀取到了 cardNumber,結果把卡號印在營收報表裡,或是被傳到不該傳的地方,造成安全漏洞。
有了作用範圍後,cardNumber 只會存在於 checkout() 函式內部。
外部完全看不到它,也不能使用它。
當函式執行完畢後,這個變數就會自動消失。
這不只能保護敏感資料,也讓你更放心地在不同功能中使用各自的變數,而不怕彼此衝突或被誤改。
JavaScript 全域變數(Global Variables):整份程式都能使用的變數
在 JavaScript 中,主要有兩種常見的作用範圍:全域變數和區域變數。
當你在最外層(也就是所有函式或區塊之外)宣告一個變數,它就會成為全域變數(global variable)。
這種變數存在於整個程式的最外層,所以在任何地方幾乎都能使用它,無論你是在上面、下面、函式裡還是邏輯區塊中,都可以存取。
來看一個例子:
let greeting = "Hello!";
function sayHi() {
console.log(greeting); // → Hello!
}
sayHi();
console.log(greeting); // → Hello!這裡的 greeting 是在函式外部宣告的變數,因此它是全域變數。
你可以在函式內部讀取它,也可以在程式的其他地方使用它,這就是「全域」的意思。
使用全域變數的優點與風險
因為全域變數「到哪裡都能用」,所以在寫程式初期時會覺得它非常方便。
不管你在哪裡需要某個資料,只要在最外層宣告一次,全程都能直接使用,省下不少傳遞的麻煩。
但是,這種「到處都能用」的特性也可能造成問題,特別是當你的程式變得越來越大時:
不小心被其他地方改值
全域變數的最大風險就是,它可以被整個程式的任何地方修改。
這代表你在一個地方設定好的變數,可能會被另一個看似無關的程式片段改掉,造成錯誤卻難以發現。
隨著程式變大,變數變難管理
如果你把太多變數都設成全域的,久而久之你會搞不清楚誰用了什麼名稱,誰又不小心改了誰的值。
這會讓除錯變得非常困難。
程式容易互相干擾,尤其在多人協作時
你可能定義了一個全域變數叫 data,同事也剛好定義了一個一樣的 data,結果程式一跑,誰都搞不清楚現在的 data 是哪一個,產生難以預測的錯誤。
建議:盡量不要濫用全域變數
雖然全域變數在某些情況下很實用,例如設定常數(const APP_NAME = "MyApp")或需要整個程式共享的資料。
但在大多數情況下,更推薦使用在特定範圍內才會存在的變數,這樣可以讓你的程式更有組織,也更安全、更好除錯。
JavaScript 區域變數(Local Variables):只在特定範圍內有效的變數
和全域變數相反,區域變數是只在特定範圍內有效的變數。
這個範圍可以是兩種常見情況之一:
- 一個「函式」的範圍(function scope)
- 一對大括號
{}組成的「區塊」範圍(block scope)
JavaScript 函式作用範圍(Function Scope)
如果你在一個函式內部宣告變數(使用 var、let 或 const),那這個變數只會存在於這個函式中。
函式從 { 開始,到 } 結束,形成一個完整的封閉範圍。
function sayHi() {
let message = "Hello!";
console.log(message);
}
sayHi(); // → Hello!
console.log(message); // 錯誤,message 在這裡不存在上面這段程式中,message 是函式 sayHi 的區域變數,它只存在於函式內部。
當你在函式外部嘗試使用 message,就會出現錯誤,因為它的範圍只限於函式裡。
這種情況叫做函式作用範圍(function scope),是早期 JavaScript 變數行為的主要範圍類型,特別是當你使用 var 宣告變數時。
JavaScript 區塊作用範圍(Block Scope)
除了函式,JavaScript 中的 {} 大括號也可以形成一個區塊範圍,像是:
if判斷式for、while迴圈- 任意以
{}包起來的區塊
當你使用 let 或 const 宣告變數時,這些變數就只會存在於這對大括號 {} 裡面。
if (true) {
let status = "logged in";
console.log(status); // → logged in
}
console.log(status); // 錯誤,status 不存在於這裡上面這個例子中,status 是用 let 宣告的,它只存在於 if 區塊內,出了這對 {} 就失效了,這就是所謂的區塊作用範圍(block scope)。
小提醒:var 不遵守區塊範圍!
值得注意的是,var 雖然也可以在區塊中宣告變數,但它不會受到區塊範圍的限制,而是會跳出區塊、回到最近的函式或全域範圍。
這點和 let、const 非常不同,後面會再詳細介紹。
if (true) {
var name = "Alice";
}
console.log(name); // → Alice 雖然在 if 裡宣告,外面還是看得到小結:函式範圍 vs 區塊範圍
注意:
var不會被區塊範圍限制,只會被函式範圍限制。
理解這兩種「區域範圍」的差異,能幫助你更有信心控制變數的使用範圍,也能避免常見的錯誤,例如:變數意外被覆蓋、變數跑出預期範圍等問題。
下一段,我們就會進一步比較 var、let、const 三者的差異,幫助你選擇最適合的宣告方式來寫出安全、穩定的 JavaScript 程式碼。
var、let、const:三種變數宣告方式的作用範圍差異
在 JavaScript 中,宣告變數有三種方式:var、let 和 const。
雖然它們都能用來建立變數,但它們的作用範圍其實不一樣,這會直接影響你寫出來的程式是安全還是容易出錯。
var 的範圍比較寬鬆(不受區塊限制)
在 JavaScript 中,var 是最早期就存在的變數宣告方式,它和後來加入的 let、const 不一樣,有一個非常特別且容易出錯的行為:
它沒有「區塊範圍」的概念,只有「函式範圍」與「全域範圍」。
也就是說,即使你在一個 {} 區塊裡使用 var 宣告變數,它也不會被侷限在這個區塊裡。
這種現象常常會讓人以為變數「只存在那塊裡」,結果卻在外部還能使用它,導致意料之外的行為。
範例:var 不會被限制在 {} 區塊中
if (true) {
var message = "Hello!";
console.log("區塊內:", message); // → 區塊內:Hello!
}
console.log("區塊外:", message); // → 區塊外:Hello!在這段程式碼中,我們在 if 區塊裡用 var 宣告了一個變數 message。
直覺上,你可能會以為它應該只存在於 if 裡,但實際上它「跑出區塊」,在外面依然能用。
這就是因為 var 不支援區塊作用範圍,只會看最近的「函式」或「全域」範圍。
如果這段程式碼寫在函式外部,那 message 就會直接變成全域變數。
var 不受區塊限制會帶來什麼問題?
這種範圍寬鬆的特性,看起來好像沒什麼,但實際上會造成很多潛在的錯誤,特別是:
你以為變數只在區塊內有效,其實它跑出來了
這可能讓你在區塊外不小心又使用到同一個變數名稱,結果資料被覆蓋,卻不知道是怎麼發生的。
程式可讀性變差,維護困難
當一個變數的範圍很大,你就很難判斷它是從哪裡來的、被誰改過、值是什麼時候變的。
範例:在 for 迴圈裡使用 var
for (var i = 0; i < 3; i++) {
console.log("迴圈內:", i);
}
console.log("迴圈外:", i); // → 3你可能會以為 i 是迴圈裡的變數,應該只能在 for 裡使用。
但因為用了 var,i 會「衝出」迴圈,在外面依然存在,而且值還是最後一次迴圈結束時的結果。
let 和 const 有真正的「區塊範圍」
在 JavaScript 的早期版本中,變數宣告只有 var 一種方式,它只能依附在「函式範圍」上,這會讓某些變數不小心跑到外面被使用,造成錯誤。
為了解決這個問題,JavaScript 在 ES6(2015年) 推出了兩個新的變數宣告方式:let 和 const。
這兩者最大的特點之一就是——它們擁有真正的「區塊作用範圍(block scope)」。
什麼是區塊範圍?
區塊(block)是指由一對大括號 {} 包起來的程式區段,像是:
if (...) { ... }for (...) { ... }while (...) { ... }- 或你自己用
{}包起來的區塊
如果你用 let 或 const 在這些區塊裡宣告變數,這些變數就只會存在於該區塊內部,離開這對 {},變數就會「失效」。
範例:let 和 const 限定在區塊內有效
if (true) {
let greeting = "Hi!";
const name = "Amy";
console.log(greeting, name); // → Hi! Amy
}
console.log(greeting); // 錯誤:greeting is not defined
console.log(name); // 錯誤:name is not defined在這個例子中,我們在 if 區塊內宣告了 greeting 和 name:
greeting用let宣告name用const宣告
它們都只存在於 if 的 {} 區塊內,一旦程式跑出這個區塊,再使用這兩個變數就會出錯,因為它們已經「離線」了。
小結:為什麼推薦使用 let 和 const?
- 它們擁有明確的區塊範圍,不容易誤用
- 它們的行為更符合人類直覺:「在 if 裡宣告,就只在 if 裡有效」
- 它們能更清楚地表達你的意圖(是否允許修改)
因此,在現代 JavaScript 開發中,幾乎都使用 let 和 const,完全不再使用 var,除非你在維護舊版系統。
在理解 let 和 const 的區塊範圍之後,你的程式將會更有結構、更安全,也更容易閱讀與維護。
結語
搞懂作用範圍是成為 JavaScript 開發者的第一步。
無論是函式範圍還是區塊範圍,清楚知道變數在哪裡有效、哪裡無效,能幫助你寫出更乾淨且無錯誤的程式碼。
記住幾個關鍵點:
- 使用
let和const建立區塊範圍變數。 var是舊式寫法,不受區塊範圍限制,容易造成變數意外外洩。- 全域變數方便但風險高,盡量限制變數的使用範圍。
- 函式內部的變數外部看不到,區塊內部的
let/const變數也一樣。
透過這些觀念,你的 JavaScript 寫作能力將更加穩固!