變數是資料結構,可為值指派代表名稱。可包含任何類型的資料。
變數名稱稱為識別碼。有效的 ID 必須遵循下列規則:
- 識別碼可包含萬國碼字母、美元符號 ($)、底線字元 (_)、數字 (0-9),甚至是部分萬國碼字元。
- 識別碼不得包含空白字元,因為剖析器會使用空白字元分隔輸入元素。舉例來說,如果您嘗試呼叫變數
my Variable
而非myVariable
,剖析器會看到兩個 ID (my
和Variable
),並擲回語法錯誤 (「unexpected token: identifier」)。 ID 開頭必須是英文字母、底線 (
_
) 或美元符號 ($
),且不得以數字開頭,以免與數字混淆:let 1a = true; > Uncaught SyntaxError: Invalid or unexpected token
如果 JavaScript 允許在識別碼開頭使用數字,那麼識別碼就會只由數字組成,導致用作數字的數字與用作識別碼的數字之間發生衝突:
let 10 = 20 10 + 5 > ?
已在語法上具有意義的「保留字」無法用做 ID。
ID 不得包含特殊字元 (
! . , / \ + - * =
)。
以下並非建立 ID 的嚴格規則,而是業界最佳做法,可讓您更輕鬆地維護程式碼。如果您的專案有不同的標準,請改為遵循這些標準,以確保一致性。
根據 JavaScript 內建方法和屬性設定的範例,駝峰式大小寫 (也稱為「駝峰式大小寫」) 是組成多個字詞的識別子非常常見的慣例。駝峰式大小寫是指將每個字詞的第一個字母大寫,除了第一個字詞,以便在不使用空格的情況下提高可讀性。
let camelCasedIdentifier = true;
部分專案會根據資料的背景和性質採用其他命名慣例。舉例來說,類別的首字母通常會大寫,因此多字詞類別名稱通常會使用駝峰式變體,也就是通常稱為「大駝峰式」或Pascal 命名法的變體。
class MyClass {
}
識別碼應簡明地描述所含資料的性質 (例如 currentMonthDays
比 theNumberOfDaysInTheCurrentMonth
更適合作為名稱),並讓使用者一目瞭然 (originalValue
比 val
更適合)。在本模組中使用的 myVariable
識別碼適用於個別範例的情況,但在實際運作程式碼中卻毫無幫助,因為它們不會提供所含資料的相關資訊。
別名不應過於明確指出所含資料,因為別名的值可能會因指令碼對該資料的操作方式,或未來維護者做出的決定而變更。舉例來說,原本具有 miles
識別碼的變數,可能需要在專案後續變更為公里值,因此維護者必須變更任何對該變數的參照,以免日後造成混淆。如要避免這種情況,請改用 distance
做為 ID。
JavaScript 不會為以底線字元 (_
) 開頭的 ID 提供任何特殊權限或意義,但這些 ID 通常用於表示變數、方法或屬性為「私有」,也就是說,這些 ID 只能在包含該 ID 的物件情境中使用,且不得在該情境之外存取或修改。這是從其他程式設計語言沿用下來的慣例,早於 JavaScript 私人屬性的新增。
變數宣告
您可以透過多種方式讓 JavaScript 瞭解識別碼,這個程序稱為「宣告」變數。變數會使用 let
、const
或 var
關鍵字宣告。
let myVariable;
使用 let
或 var
宣告可隨時變更的變數。這些關鍵字會告訴 JavaScript 解譯器,字串字元是可能包含值的 ID。
在現代化程式碼庫中作業時,請使用 let
而非 var
。var
仍可在現代瀏覽器中運作,但它具有一些不直觀的行為,這些行為是在 JavaScript 最早期的版本中定義,且無法在日後變更以保留回溯相容性。let
已在 ES6 中新增,以解決 var
設計的一些問題。
宣告的變數會透過為變數指派值初始化。使用單一等號 (=
) 為變數指派或重新指派值。您可以將此做為宣告該變數的陳述式一部分:
let myVariable = 5;
myVariable + myVariable
> 10
您也可以使用 let
(或 var
) 宣告變數,但不立即初始化。如此一來,變數的初始值就是 undefined
,直到程式碼指派值為止。
let myVariable;
myVariable;
> undefined
myVariable = 5;
myVariable + myVariable
> 10
具有 undefined
值的變數與未定義的變數不同,後者尚未宣告 ID。參照未宣告的變數會導致錯誤。
myVariable
> Uncaught ReferenceError: myVariable is not defined
let myVariable;
myVariable
> undefined
將 ID 與值建立關聯通常稱為「繫結」。let
、var
或 const
關鍵字後面的語法稱為「繫結清單」,可允許多個以半形逗號分隔的變數宣告 (結尾為預期的分號)。這會使下列程式碼片段在功能上相同:
let firstVariable,
secondVariable,
thirdVariable;
let firstVariable;
let secondVariable;
let thirdVariable;
重新指派變數值時不會使用 let
(或 var
),因為 JavaScript 已知變數存在:
let myVariable = true;
myVariable
> true
myVariable = false;
myVariable
> false
您可以根據變數的現有值,重新指派變數的新值:
let myVariable = 10;
myVariable
> 10
myVariable = myVariable * myVariable;
myVariable
> 100
如果您嘗試在實際工作環境中使用 let
重新宣告變數,就會發生語法錯誤:
let myVariable = true;
let myVariable = false;
> Uncaught SyntaxError: redeclaration of let myVariable
瀏覽器的開發人員工具對 let
(和 class
) 重新宣告的容許度較高,因此您可能不會在開發人員控制台中看到相同的錯誤。
為維持舊版瀏覽器的相容性,var
允許在任何情境下不帶錯誤地重新宣告不必要的項目:
var myVariable = true;
var myVariable = false;
myVariable\
> false
const
使用 const
關鍵字宣告常數,這是必須立即初始化,且之後無法變更的變數類型。常數的 ID 會遵循所有與使用 let
(和 var
) 宣告的變數相同的規則:
const myConstant = true;
myConstant
> true
您無法宣告常數,而不立即指派值,因為常數在建立後就無法重新指派,因此任何未初始化的常數都會永遠保持 undefined
。如果您嘗試宣告常數,但未先初始化,就會收到語法錯誤:
const myConstant;
Uncaught SyntaxError: missing = in const declaration
嘗試以 let
(或 var
) 宣告的變數值變更方式,變更以 const
宣告的變數值,會導致類型錯誤:
const myConstant = true;
myConstant = false;
> Uncaught TypeError: invalid assignment to const 'myConstant'
不過,當常數與物件建立關聯時,該物件的屬性「可以」變更。
const constantObject = { "firstvalue" : true };
constantObject
> Object { firstvalue: true }
constantObject.secondvalue = false;
constantObject
> Object { firstvalue: true, secondvalue: false }
包含物件的常數是不可變動的對可變資料值的參照。雖然常數本身無法變更,但您可以變更、新增或移除參照物件的屬性:
const constantObject = { "firstvalue" : true };
constantObject = false
> Uncaught TypeError: invalid assignment to const 'constantObject'
如果您不預期變數會重新指派,最佳做法是將其設為常數。使用 const
可讓開發團隊或未來專案維護人員知道不要變更該值,以免違反程式碼對其使用方式所做的假設,例如變數最終會根據預期資料類型進行評估。
變數範圍
變數的範圍是指可使用該變數的指令碼部分。在變數範圍之外,系統不會將其定義為包含 undefined
值的 ID,而是視為未宣告的變數。
視您用來宣告變數的關鍵字和定義變數的上下文而定,您可以將變數的範圍設為區塊陳述式 (區塊範圍)、個別函式 (函式範圍) 或整個 JavaScript 應用程式 (全域範圍)。
區塊範圍
您使用 let
或 const
宣告的任何變數,其範圍都會是最近的包含區塊陳述式,也就是說,變數只能在該區塊內存取。嘗試在包含區塊外存取區塊範圍變數,會導致與嘗試存取不存在的變數相同的錯誤:
{
let scopedVariable = true;
console.log( scopedVariable );
}
> true
scopedVariable
> ReferenceError: scopedVariable is not defined
就 JavaScript 而言,區塊範圍變數「不會」存在於包含該變數的區塊之外。舉例來說,您可以在區塊內宣告常數,然後在該區塊外宣告另一個使用相同 ID 的常數:
{
const myConstant = false;
}
const myConstant = true;
scopedConstant;
> true
雖然已宣告的變數無法擴展至父項區塊,但所有子項區塊都可以使用該變數:
{
let scopedVariable = true;
{
console.log( scopedVariable );
}
}
> true
您可以在子區塊中變更已宣告的變數值:
{
let scopedVariable = false;
{
scopedVariable = true;
}
console.log( scopedVariable );
}
> true
即使新變數使用與父區塊中變數相同的 ID,也可以在子區塊內使用 let
或 const
進行初始化,不會發生錯誤:
{
let scopedVariable = false;
{
let scopedVariable = true;
}
console.log( scopedVariable );
}
> false
函式範圍
使用 var
宣告的變數會在最接近的包含函式 (或 class 內的靜態初始化區塊) 中定義範圍。
function myFunction() {
var scopedVariable = true;
return scopedVariable;
}
scopedVariable;
> ReferenceError: scopedVariable is not defined
即使呼叫函式後,情況仍是如此。即使變數在函式執行時已初始化,在函式範圍以外仍無法使用該變數:
function myFunction() {
var scopedVariable = true;
return scopedVariable;
}
scopedVariable;
> ReferenceError: scopedVariable is not defined
myFunction();
> true
scopedVariable;
> ReferenceError: scopedVariable is not defined
全域範圍
全域變數可在整個 JavaScript 應用程式中使用,包括所有區塊和函式,以及網頁上的任何指令碼。
雖然這似乎是理想的預設值,但應用程式任何部分都能存取及修改的變數,可能會增加不必要的額外負擔,甚至會與應用程式中其他具有相同 ID 的變數發生衝突。這項規定適用於網頁算繪作業中涉及的所有 JavaScript,包括第三方程式庫和使用者分析等。因此,建議您盡可能避免污染全域範圍。
在父函式外部使用 var
宣告的任何變數,或在父區塊外部使用 let
或 const
的變數,都是全域變數:
var functionGlobal = true; // Global
let blockGlobal = true; // Global
{
console.log( blockGlobal );
console.log( functionGlobal );
}
> true
> true
(function() {
console.log( blockGlobal );
console.log( functionGlobal );
}());
> true
> true
如未明確宣告變數,就為其指派值 (也就是從未使用 var
、let
或 const
建立變數),變數就會提升至全域範圍,即使在函式或區塊內初始化也一樣。使用這個模式建立的變數有時稱為「隱含的全球變數」。
function myFunction() {
globalVariable = "global";
return globalVariable
}
myFunction()\
> "global"
globalVariable\
> "global"
變數提升
變數和函式宣告會提升至其範圍的頂端,也就是說,JavaScript 轉譯器會處理指令碼中任何位置宣告的變數,並在執行指令碼前,將該變數有效地移至其包函範圍的第一行。也就是說,使用 var
宣告的變數可以在變數宣告前參照,而不會發生錯誤:
hoistedVariable
> undefined
var hoistedVariable;
由於只會代管變數宣告,而非初始化,因此未以 var
、let
或 const
明確宣告的變數不會提升:
unhoistedVariable;
> Uncaught ReferenceError: unhoistedVariable is not defined
unhoistedVariable = true;
如先前所述,已宣告但未初始化的變數會指派 undefined
值。這項行為也適用於提升變數宣告,但僅適用於使用 var
宣告的變數。
hoistedVariable
> undefined
var hoistedVariable = 2 + 2;
hoistedVariable\
> 4
這種不直觀的行為,主要是因為 JavaScript 早期版本的設計決策,如果要變更,就可能會導致現有網站發生問題。
let
和 const
會在變數建立前存取變數時擲回錯誤,以便解決這項行為:
{
hoistedVariable;
let hoistedVariable;
}
> Uncaught ReferenceError: can't access lexical declaration 'hoistedVariable' before initialization
這項錯誤與嘗試存取未宣告的變數時可能會發生的「hoistedVariable is not defined」錯誤不同。由於 JavaScript 已提升變數,因此會知道變數會在指定範圍內建立。不過,如果在宣告前未將該變數設為 undefined
,轉譯器就會擲回錯誤。以 let
或 const
(或 class
) 宣告的變數,會從包函區塊的開頭到宣告變數的程式碼點,存在於「時間死區」(「TDZ」) 中。
時間死區可讓 let
的行為對作者來說更符合直覺,比 var
更容易理解。這對 const
的設計也至關重要。由於常數無法變更,因此將常數提升至其範圍頂端,並指定 undefined
的隱含值,就無法以有意義的值初始化。
進行隨堂測驗
您可以使用哪些字元開頭來建立 ID?
如要宣告可隨時變更值的變數,哪一種方法較佳?