已編入索引的集合

已建立索引的集合是一種資料結構,系統會使用編號的索引儲存及存取元素。系統會從 0 開始為已建立索引的集合指派編號索引,這個模式稱為「零索引」。 接著,您可以參照索引集合中的值,存取儲存在索引集合中的值。

陣列

陣列是一種容器,可容納任何資料類型的零或多個值,包括複雜的物件或其他陣列。陣列中的值有時稱為陣列的「元素」。

建立陣列

與原始資料類型一樣,有兩種方法可以建立陣列:做為陣列常值,或是使用 new Array() 叫用 JavaScript 內建的 Array() 建構函式。將陣列指派給變數可讓您具有高度可攜性和「可疊代」方法,將多個值指派給單一 ID。

陣列常值語法會使用一組括號 ([]),括住零個或多個以半形逗號分隔的資料值:

const myArray = [];

陣列建構函式語法使用 JavaScript 的內建 Array 物件做為搭配 new 關鍵字使用的建構函式:

const myArray = new Array();

陣列常值和陣列建構函式語法都可讓您在陣列建立時填入資訊,不過這些值的定義方式稍有不同。陣列常值語法會在括號之間使用以半形逗號分隔的值,看起來與產生的陣列相同:

const myArray = [ true, null, "String", false ];

myArray;
> [ true, null, "String", false ]

陣列建構函式語法會將半形逗號分隔值視為引數,但有一個特殊的行為例外狀況:

const myArray = new Array( true, null, "String", false );

myArray;
> Array(4) [ true, null, "String", false ]

將單一數值值傳遞至 Array 建構函式時,該值不會指派給結果陣列中的第零位置。相反地,系統會使用此數量的空白運算單元來建立陣列。這不會對陣列加上任何限制。項目可以在其中新增和移除,方法與使用陣列常值相同。

// Firefox:\
const myArray = new Array( 10 );

myArray;
> Array(10) [ <10 empty slots> ]
// Chrome:
const myArray = new Array( 10 );

myArray;
> (10) [empty × 10]

包含空白運算單元的陣列 (有時稱為「稀疏陣列」) 是特殊情況。在其他語言中,空白位置通常會視為 undefined 值,而非包含 undefined 或明確的 null 值。

建立陣列常值時,您可能會不小心使用陣列常值語法來建立 sparse 陣列,

const myArray = [ true,, true, false ];

myArray;
> Array(4) [ true, <1 empty slot>, true, false ]

雖然在所有情況下,系統不會將空白版位視為有意義的值,但會將空白運算單元計入陣列的總長度中,因此在疊代陣列的值時,可能會產生非預期的結果:

const myArray = [ 1,, 3, 4 ];

myArray.length;
> 4

for( const myValue of myArray ) {
  console.log( myValue + 10 );
}
> 11
> NaN
> 13
> 14

這種行為是 JavaScript 早期設計決策的一部分。避免在現代開發中使用稀疏陣列。

與基元一樣,陣列常值會繼承其對應建構函式的屬性和方法。由於陣列是特殊物件形式,因此陣列常值語法和 new Array() 語法會產生完全相同的結果:從 Array 建構函式繼承其原型的物件。

const arrayLiteral = [];
const arrayConstructor = new Array();

typeof arrayLiteral;
> "object"

arrayLiteral;
> Array []
    length: 0
    <prototype>: Array []

typeof arrayConstructor;
> "object"

arrayConstructor;
> Array []
    length: 0
    <prototype>: Array []

由於兩個結果相同,且陣列常值語法較為精簡和常值,因此強烈建議您一律使用陣列常值語法,而非 new Array() 語法。

存取陣列值

如要存取陣列中的個別元素,您可以使用括號標記法、陣列後方的一組括號 ([]),或是含有該元素的索引數字的 ID ID:


[ "My string", "My other string" ][ 1 ];
> "My other string"

const myArray = [ "My string", 50, true ];

myArray[ 0 ];
> "My string"

myArray[ 1 ];
> 50

myArray[ 2 ];
> true

JavaScript 中的陣列不是關聯的,因此您無法使用任意字串做為索引。不過,用來存取陣列中元素的數值會強制轉換成幕後的字串值,也就是說,您可以使用僅包含數字字元的字串值:

const myArray = [ "My string", 50, true ];

myArray[ 2 ];
> true

myArray[ "2" ];
> true

如果嘗試存取陣列中定義的元素外的元素,將導致 undefined 而非錯誤:

const myArray = [ "My string", 50, true ];

myArray[ 9 ];
> undefined

正在解構指派作業

解構指派是一種簡潔的方式,可從陣列或物件中擷取特定範圍的值,並將其指派給一組 ID。有時也稱為「解除封裝」原始資料結構的程序,雖然不會修改原始陣列或物件,

解構指派作業會使用陣列或物件類的 ID 清單來追蹤值。以最簡單的形式稱為「繫結模式」解碼,每個值會從陣列或物件解除封裝,並指派給對應的變數,再使用 letconst (或 var) 初始化:

const myArray = [ "A string", "A second string" ];
const [ myFirstElement, mySecondElement ] = myArray;

const myObject = { firstValue: false, secondValue: true };
const { myProp, mySecondProp } = myObject;

myFirstElement;
> "My string"

mySecondElement;
> "Second string"

myProp;
> false

mySecondProp;
> true

使用大括號 ({}) 解構物件,使用方括號 ([]) 則可解構陣列。

const myArray = [ false, true ];
const myObject = { firstValue: false, secondValue: true };

const [ myProp, mySecondProp ] = myObject;
> Uncaught TypeError: myObject is not iterable

const { myElement, mySecondElement } = myArray;

myElement
> undefined

mySecondElement;
> undefined

解構陣列的順序是由左至右依序執行。解構指派中的每個 ID 都會對應到具有相同索引的陣列元素:

const myArray = [ 1, 2, 3 ];
const [ myElement, mySecondElement, myThirdElement ] = myArray;

myElement;
> 1

mySecondElement;
> 2

myThirdElement;
> 3

這是解構物件時的預設行為。不過,如果刪除作業中使用的 ID 與物件屬性的鍵相符,則無論指定順序為何,這些 ID 都會填入對應的屬性值:

const myObject = { firstValue: 1, secondValue: 2, thirdValue 3 };
const { secondValue, thirdValue, firstValue } = myObject;

firstValue;
> 1

secondValue;
> 2

thirdValue;
> 3

省略 ID 即可略過元素:

const myArray = [ 1, 2, 3 ];
const [ firstValue,, secondValue ] = myArray;

firstValue;
> 1

secondValue;
> 3

解構語法也可讓您指派預設值,以便在非結構化的值為空白版位 (如稀疏陣列的情況下) 或 undefined 值時使用。

const myArray = [ true, ];
const [ firstValue = "Default string.", secondValue = "Default string." ] = myArray;

firstValue;
> true

secondValue;
> "Default string."

解構不會將值強制轉換成特定類型。這表示"falsy" 值,例如空白字串 ("") 或 null,仍然被視為有意義的去建構值:

const myArray = [ false, null, 0, "",, undefined ];
const [ falseValue = true, nullValue = true, zeroValue = true, emptyStringValue = true, emptySlot = true, undefinedValue = true ] = myArray;

falseValue;
> false;

nullValue;
> null

zeroValue;
> 0

emptyStringValue;
> ""

emptySlot;
> true

undefinedValue;
> true

擴散運算子

使用 ES6 中引入的傳播運算子 (...),將可疊代的資料結構 (例如陣列、字串或物件常值) 擴展至個別元素。傳播運算子緊接在要展開的資料結構之後,或是包含該資料結構的變數 ID。

const myArray = [ 1, 2, 3 ];

console.log( ...myArray );
> 1 2 3

傳播語法主要用於複製和合併陣列:

const myArray = [ 4, 5, 6 ];
const mySecondArray = [1, 2, 3, ...myArray ];

mySecondArray;
> Array(6) [ 1, 2, 3, 4, 5, 6 ]

傳播語法只能在下列情況下使用:

如為陣列和字串,傳播語法僅適用於函式呼叫中的零或多個引數,或陣列中的元素預期會發生的情況。本節中的第 1 個 Spread 運算子語法範例能夠正常運作,因為此範例會將 ...myArray 做為引數傳遞至內建 console.log 方法。

舉例來說,您無法將要傳播的資料指派給其他陣列以外的變數:

const myArray = [ 1, 2, 3 ];
const spreadVariable = ...myArray;
> Uncaught SyntaxError: Unexpected token '...'

但是,您可以將原始陣列分散至陣列常值,來複製陣列:

const myArray = [ 1, 2, 3 ];
const spreadArray = [ ...myArray ];

spreadArray;
> Array(3) [ 1, 2, 3 ]

如何將包含兩個或多個陣列的元素合併為單一陣列:

const myArray = [ 1, 2, 3 ];
const mySecondArray = [ 4, 5, 6 ];
const myNewArray = [ ...myArray, ...mySecondArray ];

myNewArray;
> Array(6) [ 1, 2, 3, 4, 5, 6 ]

或是在函式呼叫中,將陣列的元素做為個別引數傳遞:

const myArray = [ true, false ];
const myFunction = ( myArgument, mySecondArgument ) => {
    console.log( myArgument, mySecondArgument );
};

myFunction( ...myArray );
> true false

擴散運算子的應用範圍擴大,以便與 ES2018 中的物件常值搭配使用。與陣列一樣,您可以使用分散運算子來複製或合併物件:

const myObj = { myProperty : true };
const mySecondObj = { ...myObj };

mySecondObj;
> Object { myProperty: true }
const myFirstObj = { myProperty : true };
const mySecondObj = { additionalProperty : true };
const myMergedObj = { ...myFirstObj, ...mySecondObj };

myMergedObj;
> Object { myProperty: true, additionalProperty: true }

傳播運算子會建立「淺層」副本。這表示這個元件不會複製原始物件的原型以及非列舉屬性。

const myCustomPrototype = { protoProp: "My prototype." };
const myObj = Object.create( myCustomPrototype, {
    myEnumerableProp: {
        value: true,
        enumerable: true
    },
    myNonEnumerableProp: {
        value: false,
        enumerable: false
    }
});
const myNewObj = { ...myObj };

myObj;
> Object { myEnumerableProp: true, … }
    myEnumerableProp: true
    myNonEnumerableProp: false
    <prototype>: Object { protoProp: "My prototype." }

myNewObj;
> Object { myEnumerableProp: true }
    myEnumerableProp: true
    <prototype>: Object { … }

請注意,陣列和物件無法交替使用。您無法將物件分散至陣列,也無法將陣列擴及物件。

休息運算子

雖然運算子本身的語法相同,但其餘運算子 (...) 會根據所用結構定義執行相反的函式。其餘運算子不會像解構指派函式參數一樣,將可疊代的資料結構擴展為個別元素,而是將元素合併成可疊代的資料結構。這個名稱來自於用來收集一組資料值的「其餘」資料。

與解構指派搭配使用時,語法稱為「rest 屬性」語法。

const myArray = [ "First", "Second", "Third", "Fourth", "Fifth" ];

[ myFirstElement, mySecondElement, ...remainingElements ] = myArray;

myFirstElement;
> "First"

mySecondElement;
> "Second"

remainingElements;
> Array(3) [ "Third", "Fourth", "Fifth"]

用於為函式提供指定數量的引數時,語法稱為「其他參數」語法:

function myFunction( ...myParameters ) {
    let result = 0;
    myParameters.forEach( ( myParam ) => {
        result += myParam;
    });
    return result;
};

myFunction( 2, 2 );
> 4

myFunction( 1, 1, 1, 10, 5 );
> 18

myFunction( 10, 11, 25 );
> 46

%TypedArray%

型別陣列是一項 ES6 功能,用於儲存結構化二進位資料,例如在處理上傳的檔案或 WebGL 時。

符號一樣,%TypedArray% 內建函式 (通常以 %TypedArray%@@TypedArray 記錄,因此不會誤認為全域屬性) 並不是建構函式函式,而且您無法以 new 或直接呼叫該函式。相反地,%TypedArray% 是指個別建構函式的父項父類別,每個建構函式都能與特定二進位資料格式搭配使用。內建 %TypedArray% 父類別提供屬性和公用程式方法,所有 %TypedArray% 建構函式子類別及其執行個體都會沿用這些方法。

隨堂測驗

已知 `const myArray = [ 30, 50, 70 ];` `myArray[1]` 會傳回什麼內容?

50
30
70

如果 `myArray` 有三個值,`myArray[9]` 會傳回什麼?

Undefined
顯示錯誤訊息
9
Null