Variables

變數是資料結構,可為值指派代表名稱。可包含任何類型的資料。

變數名稱稱為識別碼。有效的 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 scopedConstant = false;
}
const scopedConstant = 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?

一封信
底線
一個位數

如要宣告可隨時變更值的變數,哪一種方法較佳?

let
const
var