本文將說明範圍及其在 JavaScript 中的運作方式。
範圍是 JavaScript 和其他程式設計語言中的基本概念,用於定義存取和使用變數的結構定義。隨著您持續學習 JavaScript 及運用變數,您的程式碼會變得更加實用,且適用於程式碼。
範圍可協助您:
- 更有效率地使用記憶體:範圍可讓您只在需要時載入變數。如果變數超出範圍,則不需要提供給目前執行的程式碼使用。
- 更輕鬆地找出及修正錯誤:使用本機範圍隔離變數,可讓您更輕鬆地排解程式碼中的錯誤;因為與全域變數不同,您可以信任外部範圍的程式碼無法操控本機範圍變數。
- 建立可重複使用的程式碼小區塊:例如,您可以編寫不必依賴外部範圍的純函式。只需稍微調整,即可輕鬆將這類函式移至其他位置。
什麼是範圍?
變數的範圍會決定您可以在程式碼中使用變數的哪個位置。
JavaScript 會定義全域或本機範圍的變數:
- 設有全域範圍的變數可從 JavaScript 程式碼中的其他範圍中使用。
- 設有本機範圍的變數僅適用於特定當地環境,且可透過關鍵字建立,例如
var
、let
和const
。如果您使用var
、let
或const
關鍵字在函式中建立變數,則該變數會具有本機範圍。
本文後續章節將探討區塊和詞法範圍:
- 「區塊範圍」變數可在本機供本機使用,具體取決於大括號內定義區塊陳述式的位置。只有以
let
或const
關鍵字宣告的變數具有區塊範圍。 - Lexical 範圍會使用原始碼中宣告變數的位置,判斷該變數的適用位置。您可以使用閉包來授權封閉式函式存取外部範圍 (稱為詞法環境) 中參照的變數。
如果在範圍內存取變數,JavaScript 會傳回其指定值或產生錯誤。
宣告變數的方法如下:
- 使用
var
、const
或let
關鍵字來宣告本機或全域範圍變數。 - 使用
const
或let
關鍵字宣告區塊範圍變數。
當您在函式中宣告 var
變數時,宣告會將變數提供給最近的封閉函式。您無法使用 var
關鍵字宣告封鎖範圍的變數。
範圍範例
這個範例說明瞭全域範圍,因為 greeting
變數是在函式或區塊外宣告,所以其值可供目前文件中的所有程式碼使用:
const greeting = 'hello';
console.log(greeting); // 'hello'
在全域範圍範例中,greeting
變數獲派 hello
值。
本範例示範本機範圍,因為在函式中使用 let
關鍵字宣告 greeting
變數。greeting
變數是本地範圍的變數,無法在函式外部使用。
function greet() {
let greeting = 'Hello World!';
console.log(greeting);
}
以下範例說明區塊範圍,原因是此範例會在區塊中宣告 greeting
變數,讓該變數只能在大括號中存取:
if (true) {
const greeting = 'hello';
}
console.log(greeting); // ReferenceError: greeting is not defined
請注意,當 console.log
函式嘗試輸出 greeting
變數的值時,JavaScript 會傳回 ReferenceError
錯誤訊息,而不是預期的 hello
訊息。原因何在?
greeting
變數具有區塊範圍,且最近的區塊是 if
條件陳述式的一部分,因此系統會傳回錯誤。您無法從區塊外存取區塊中宣告的 let
和 const
變數。因此,您只能在指定區塊範圍的大括號中存取 greeting
變數。
此範例會將 console.log(message)
方法移至大括號中,因此修正了錯誤。更新的程式碼會將 console.log(message)
方法重新置於區塊中。
if (true) {
const greeting = 'hello';
console.log(greeting);
}
範圍類型
全域範圍
您可以在程式中的任何位置存取全域範圍的變數。
假設使用一個 HTML 檔案匯入兩個 JavaScript 檔案:file-1.js
和 file-2.js
:
<script src="file-1.js"></script>
<script src="file-2.js"></script>
在這個範例中,globalMessage
變數具有全域範圍,而且是在函式外編寫。在執行和執行期間,您可以從 JavaScript 程式的任何位置存取 globalMessage
變數的值。
您可以在這個程式碼片段中查看 file-1.js
和 file-2.js
檔案的內容。請注意,這兩個檔案中都有 globalMessage
變數。
// file-1.js
function hello() {
var localMessage = 'Hello!';
}
var globalMessage = 'Hey there!';
// file-2.js
console.log(localMessage); // localMessage is not defined
console.log(globalMessage); // Hey there!
還有其他類型的範圍不在本文的討論範圍內。如果您在 JavaScript 模組內建立變數,但不在函式或區塊之外,則變數沒有全域範圍,而是模組範圍。具有模組範圍的變數可在目前模組中的任何位置存取,但無法透過其他檔案或模組存取。如要將模組範圍變數提供給其他檔案使用,您必須從建立該變數的模組匯出該變數,然後從需要存取變數的模組中import。
本機範圍和函式範圍
當您在 JavaScript 函式中使用 var
、let
或 const
關鍵字建立變數時,這些變數是函式本機的,因此您只能從函式中存取這些變數。本機變數會在函式啟動時建立,並在函式執行完畢後有效刪除。
這個範例會在 addNumbers()
函式中宣告 total
變數。您只能存取 addNumbers()
函式中的 a
、b,
和 total
變數。
function addNumbers(a, b) {
const total = a + b;
}
addNumbers(3, 4);
您可以使用 let
和 const
關鍵字為變數命名。使用 let
關鍵字時,JavaScript 可以更新變數。然而,有了 const
關鍵字,變數會保持不變。
var variable1 = 'Declared with var';
var variable1 = 'Redeclared with var';
variable1; // Redeclared with var
let variable2 = 'Declared with let. Cannot be redeclared.';
variable2 = 'let cannot be redeclared, but can be updated';
variable2; // let cannot be redeclared, but can be updated
const variable3 = 'Declared with const. Cannot be redeclared or updated';
variable3; // Declared with const. Cannot be redeclared or updated
封鎖範圍
封鎖條件可用來將單一陳述式或一組陳述式分組。您可以使用 const
或 let
關鍵字來宣告區塊範圍的本機變數。請注意,您無法使用 var
關鍵字宣告區塊範圍的變數。
舉例來說,在這個區塊中,name
變數的範圍及其 "Elizabeth"
值包含在大括號中。區塊範圍內的變數無法在區塊外使用。
{
const name = "Elizabeth";
}
您可以在 if
、for
或 while
陳述式中使用區塊範圍變數。
請記下這個程式碼片段中的兩個 for
迴圈。一個 for
迴圈會使用 var
關鍵字宣告初始化器變數,而變數會透過 0
、1
和 2
遞增。其他 for
迴圈會使用 let
關鍵字宣告初始化器變數。
for (var i = 0; i < 2; i++) {
// ...
}
console.log(i); // 2
for (let j = 0; j < 2; j++) {
// ...
}
console.log(j); // The j variable isn't defined.
在先前的程式碼範例中,您可能會發現第一個 for
迴圈中的 i
變數外洩至 for
迴圈,但仍會保留 2
值,因為 var
關鍵字未使用區塊範圍。這項問題在第二個 for
迴圈中已修正,其中使用 let
關鍵字宣告的 j
變數的範圍限定在 for
迴圈的區塊內,且在 for
迴圈完成後不存在。
重複使用其他範圍內的變數名稱
範圍可以區隔函式中的變數,即使您在不同範圍的其他位置重複使用相同的變數名稱也一樣。
本例說明如何使用範圍,在不同函式中重複使用相同的變數名稱:
function listOne() {
let listItems = 10;
console.log(listItems); // 10
}
function listTwo() {
let listItems = 20;
console.log(listItems); // 20
}
listOne();
listTwo();
listOne()
和 listTwo()
函式中的 listItems
變數會獲派預期值,因此請勿彼此衝突。
閉包和詞法範圍
閉包是指封閉式函式,內部函式可存取外部函式範圍,也稱為詞法環境。因此,在 JavaScript 中,您可以使用閉包來讓函式參照外部詞法環境,這樣一來,程式碼就能在函式外宣告的函式參照變數內編寫程式碼。事實上,您可以將指向外部詞法環境的參照鏈結編寫程式碼,讓函式由另一個函式呼叫函式。
在本範例中,程式碼會形成一個閉包環境,而該環境會在叫用 outer()
函式時建立,並透過 hello
變數關閉。因此,hello
變數會在 setTimeout
回呼函式中使用。
function outer() {
const hello = 'world';
setTimeout(function () {
console.log('Within the closure!', hello)
}, 100);
}
outer();
使用詞法範圍時,範圍是在原始碼編譯期間決定,而非在執行階段。如要進一步瞭解詞法環境,請參閱詞形範圍和閉包。
單元
JavaScript 模組有助於整理 JavaScript 程式碼。如果使用得當,它們可以為程式碼集提供有效的結構,並協助重複使用程式碼。JavaScript 模組提供匯出和import變數的技巧,而不是使用全域變數在不同檔案之間共用變數。
// hello.js file
function hello() {
return 'Hello world!';
}
export { hello };
// app.js file
import { hello } from './hello.js';
console.log(hello()); // Hello world!
範圍視覺化工具示範
範圍是所有 JavaScript 開發人員都應瞭解的基本概念。如要進一步瞭解範圍系統,您可以嘗試使用 JS 範圍 Visualizer 自行編寫程式碼。示範程式碼會在程式碼中使用顏色,協助您以視覺化方式呈現 JavaScript 範圍。
結語
本文將介紹不同的範圍類型。JavaScript 範圍是網頁開發中的進階概念之一,因此請務必詳閱內容,並花一些時間瞭解這個主題。
範圍並非面向使用者的功能。這只會影響編寫程式碼的網頁程式開發人員,但瞭解範圍的運作方式,可協助您修正錯誤。