完整 JavaScript 陣列 Array 詳細教學和範例

目錄:

宣告/建立陣列

    
// 建立陣列
let array1 = ['A', 'B', 'C'];

// 顯示陣列內容
console.log(array1); // (3) ['A', 'B', 'C']
    

陣列中可以儲存不同型別的元素,也不會影響(變更)元素的型別:
    
// 建立包含不同型別內容的示範陣列
let array2 = [true, 2, "三", '四', null, undefined];
console.log(array2); // (6) [true, 2, '三', '四', null, undefined]

// 列出每個元素的型別
array2.forEach((item) => {
    console.log(typeof item);
});
/*
boolean
number
string
string
object
undefined
 */
    
註:不論單引號(' ')還是雙引號(" "),型別都是字串(string),在輸出時都會以單引號顯示

陣列長度 length

取得陣列長度

    
let array1 = ['A', 'B', 'C'];

let length = array1.length;
console.log(length); // 3
    

設定陣列長度

若設定的長度小於陣列本身的長度會自動刪除超過的元素
    
let array1 = ['A', 'B', 'C'];

console.log(array1); // (3) [ "A", "B", "C" ]
console.log(array1.length); // 3

array1.length = 2;
console.log(array1); // (2) [ "A", "B" ]
console.log(array1.length); // 2
    

若設定的長度大於陣列本身的長度,則會填入空元素
    
let array1 = ['A', 'B', 'C'];

console.log(array1); // (3) [ "A", "B", "C" ]
console.log(array1.length); // 3

array1.length = 5;
console.log(array1); // (5) ['A', 'B', 'C', empty × 2]
console.log(array1.length); // 5

console.log(array1[3]); // undefined
    

註:長度需要介於 0 ~ 4294967295 (2 的 32 次方減 1)之間
不然會拋出: RangeError: Invalid array length
    
let array1 = ['A', 'B', 'C'];

array1.length = -1; // Uncaught RangeError: Invalid array length
array1.length = 4294967296; // 20230108.js:4 Uncaught RangeError: Invalid array length
    

取得指定位置的元素

註: 陣列從 0 開始,所以第一個是 0,第二個是 1
    
let array1 = ['A', 'B', 'C'];

console.log(array1[0]); // A
console.log(array1[1]); // B
console.log(array1[2]); // C
    

取得指定元素的位置

如果沒有找到元素會回傳 -1
    
let array1 = ['A', 'B', 'C'];

let index = array1.indexOf('B');
console.log(index); // 1

let index2 = array1.indexOf('D');
console.log(index2); // -1 // 找不到會回傳 -1
    

陣列增加元素

將元素放入陣列開頭: unshift

    
let array1 = ['A', 'B', 'C'];
console.log(array1); // (3) ['A', 'B', 'C']

array1.unshift('D');
console.log(array1); // (4) ['D', 'A', 'B', 'C']
    

將元素放入陣列最尾端: push

    
let array1 = ['A', 'B', 'C'];
console.log(array1); // (3) ['A', 'B', 'C']

array1.push('D');
console.log(array1); // (4) ['A', 'B', 'C', 'D']
    

將元素放入(取代)陣列指定位置

    
let array1 = ['A', 'B', 'C'];
console.log(array1); // (3) ['A', 'B', 'C']

// 將陣列的第二個變成 'D'
array1[1] = 'D';
console.log(array1); // (3) ['A', 'D', 'C']

// 將陣列的第一個變成 'E'
array1[0] = 'E';
console.log(array1); // (3) ['E', 'D', 'C']
    


如果已經有三個元素,放入第四個會和 push 一樣加在最後面,如果沒有第五個元素依然可以直接放入第六個,此時第五個會和上面設定陣列長度時一樣自動塞入空元素
    
let array1 = ['A', 'B', 'C'];
console.log(array1); // (3) ['A', 'B', 'C']

// 將陣列的第四個元素變成 'E' (但目前只有三個元素)
array1[3] = 'E';
console.log(array1); // (4) ['A', 'B', 'C', 'E']

// 將陣列的第六個元素變成 'E' (但目前只有四個元素)
array1[5] = 'F';
console.log(array1); // (6) ['A', 'B', 'C', 'E', 空白, 'F']

// 列出每個元素的型別
array1.forEach((item, index) => {
    console.log(index, typeof item);
});
/*
0 'string'
1 'string'
2 'string'
3 'string'
5 'string'
 */

console.log(array1[3]); // E
console.log(array1[4]); // undefined
console.log(array1[5]); // F
    

在第四個位置放入 'E' 後陣列長度變成 4,在第六個位置放入 'F' 後陣列長度變成 6 ,第五個位置自動放入 undefined,在使用 forEach 歷遍時不會被輸出。專有名詞叫做不可迭代(non-iterable)的空槽(empty slot)

移除元素

註:使用下列的 pop, shift, splice 都會回傳被移除的元素

移除最後一個元素 pop

    
let array1 = ['A', 'B', 'C'];
console.log(array1); // (3) ['A', 'B', 'C']
array1.pop(); // 移除最後一個元素
console.log(array1); // (2) ['A', 'B']
    

移除第一個元素 shift

    
let array1 = ['A', 'B', 'C'];
console.log(array1); // (3) ['A', 'B', 'C']
array1.shift(); // 移除第一個元素
console.log(array1); // (2) [ 'B', 'C' ]
    

移除指定位置的元素 splice

    
let array1 = ['A', 'B', 'C', 'D'];
console.log(array1); // (3) [ 'A', 'B', 'C', 'D' ]
let removeItems = array1.splice(2, 1); // 移除第三個元素,並回傳被移除的元素
console.log(removeItems); // (1) [ 'C' ] // 被移除的元素
console.log(array1); // (2) [ 'A', 'B', 'D' ]
    

移除指定位置開始的多個元素

    
let array1 = ['A', 'B', 'C', 'D'];
console.log(array1); // (3) [ 'A', 'B', 'C', 'D' ]
let removeItems = array1.splice(2); // 移除第三個到最後一個元素
console.log(removeItems); // (2) [ 'C', 'D' ] // 被移除的元素
console.log(array1); // (2) [ 'A', 'B' ]
    

移除指定內容的元素

要移除指定內容的元素需要先使用 indexOf 找到位置,再使用 splice 移除
    
let array1 = ['A', 'B', 'C'];

let index = array1.indexOf('B');
if(index !== -1) { // 元素可能不存在
  array1.splice(index, 1);
}
console.log(array1); // (2) [ 'A', 'C' ]
    

歷遍

從頭到尾依序將元素帶入處理

for 迴圈

    
let array1 = ['A', 'B', 'C'];

for (let i = 0; i < array1.length; i++) {
    console.log(array1[i]);
}
/*
A
B
C
*/
    

forEach()

最基本 forEach

element 代表當前輪到的元素
    
let array1 = ['A', 'B', 'C'];

array1.forEach(element => {
    console.log(element);
});
    

包含可選參數的 forEach

element 代表當前輪到的元素
index 為第幾個
array 則是陣列本身
    
let array1 = ['A', 'B', 'C'];

array1.forEach((element, index, array) => {
    console.log(element, index, array);
});

/*
A 0 (3) ['A', 'B', 'C']
B 1 (3) ['A', 'B', 'C']
C 2 (3) ['A', 'B', 'C']
 */
    

可以省略 array 或是 index 和 array 一起省略

包含 this 參數

    
let array1 = ['A', 'B', 'C'];

array1.forEach((element, index, array) => {
    console.log(element, index, array);
}, this);


/*
A 0 (3) ['A', 'B', 'C'] Window {window: Window, self: Window, document: document, name: '', location: Location, …}
B 1 (3) ['A', 'B', 'C'] Window {window: Window, self: Window, document: document, name: '', location: Location, …}
C 2 (3) ['A', 'B', 'C'] Window {window: Window, self: Window, document: document, name: '', location: Location, …}
*/
    

修改 forEach 中的資料

上面提到 forEach() 的第一個參數是只當前的元素(element),那我要修改的時候直接改 element 為什麼改不動?
    
console.log(array1); // (4) [ 1, 2, 3, 4 ]
array1.forEach((element)=>{
    item = item + 1;
    console.log(element);
    /*
    2
    3
    4
    5
     */
})

console.log(array1); // (4) [ 1, 2, 3, 4 ] // 沒有改變原本的陣列
    

奇怪了?明明在第 3 行修改,在第 4 行輸出中也有看到有變更,可是跑完迴圈後怎麼都沒有變?

其實我們可以把 forEach 的第一個參數 element 理解為當前資料的副本,只會在當前的區塊有效,要變更內容也很簡單,forEach 的第二個參數是該元素在此 array 中的索引(第幾個),所以我們就可以很簡單的直接操作原本的 array 了:
    
let array1 = [1, 2, 3, 4];
console.log(array1); // (4) [ 1, 2, 3, 4 ]
array1.forEach((item,index)=>{
    array1[index] = item + 1;
    console.log(item);
    /*
    2
    3
    4
    5
     */
})

console.log(array1); // (4) [ 2, 3, 4, 5 ]
    

複製陣列

首先先說明等於符號(=)不代表複製,應該是「賦值」,下面的程式碼會讓 array1 和 array2 共用,所以當任意一邊被修改時兩邊都會被修改。
    
let array1 = ['A', 'B', 'C']; // 原始內容
let array2;
let array3;

array2 = array1;
array3 = array1.slice();

console.log(array1); // [ 'A', 'B', 'C' ]
console.log(array2); // [ 'A', 'B', 'C' ]
console.log(array3); // [ 'A', 'B', 'C' ]

array1[0] = 'X';
console.log(array1); // [ 'X', 'B', 'C' ]
console.log(array2); // [ 'X', 'B', 'C' ]
console.log(array3); // [ 'A', 'B', 'C' ]

array2[1] = 'Y';
console.log(array1); // [ 'X', 'Y', 'C' ]
console.log(array2); // [ 'X', 'Y', 'C' ]
console.log(array3); // [ 'A', 'B', 'C' ]
    

使用 slice 複製

slice 有兩個參數,第一個是複製起始位置,第二個參數是複製結束位置,但不包含結束位置的那個參數,可以省略參數。
    
let array1 = ['A', 'B', 'C', 'D', 'E']; // 原始內容

let array2 = array1.slice(); // 複製全部元素
let array3 = array1.slice(1); // 從第二個元素開始複製
let array4 = array1.slice(1, 3); // 從第二個元素複製到第四個元素,但不包含第四個元素

console.log(array2); // [ 'A', 'B', 'C', 'D', 'E' ]
console.log(array3); // [ 'B', 'C', 'D', 'E' ]
console.log(array4); // [ 'B', 'C' ]
    

在絕大多數的程式語言中,複製都有兩種,分別是複製位址(傳址(reference), 淺拷貝, Shallow Copy) 和複製內容(傳值(value), 深拷貝, Deep Copy),其實只有複製第一層,裡面的內容還是引用原本的資料。有什麼區別呢?先看看下面的範例:
    
let array1 = [{id: 0, name: 'A'}, {id: 1, name: 'B'}, {id: 2, name: 'C'}]; // 原始內容
let array2;
let array3;

array2 = array1;
array3 = array1.slice();

console.log(array1); // [ { id: 0, name: 'A' }, { id: 1, name: 'B' }, { id: 2, name: 'C' } ]
console.log(array2); // [ { id: 0, name: 'A' }, { id: 1, name: 'B' }, { id: 2, name: 'C' } ]
console.log(array3); // [ { id: 0, name: 'A' }, { id: 1, name: 'B' }, { id: 2, name: 'C' } ]

array1[0].name = 'X';
console.log(array1); // [ { id: 0, name: 'X' }, { id: 1, name: 'B' }, { id: 2, name: 'C' } ]
console.log(array2); // [ { id: 0, name: 'X' }, { id: 1, name: 'B' }, { id: 2, name: 'C' } ]
console.log(array3); // [ { id: 0, name: 'X' }, { id: 1, name: 'B' }, { id: 2, name: 'C' } ]

array2[1].name = 'Y';
console.log(array1); // [ { id: 0, name: 'X' }, { id: 1, name: 'Y' }, { id: 2, name: 'C' } ]
console.log(array2); // [ { id: 0, name: 'X' }, { id: 1, name: 'Y' }, { id: 2, name: 'C' } ]
console.log(array3); // [ { id: 0, name: 'X' }, { id: 1, name: 'Y' }, { id: 2, name: 'C' } ]
    

可以發現這次居然連使用 slice 複製的內容也會被變更!
所以在實作的時候要非常小心,避免改到不該改的資料。

雖然不是本篇的內容,但是還是示範一下其中一種傳值複製的方式,一樣使用剛剛的例子:
    
let array1 = [{id: 0, name: 'A'}, {id: 1, name: 'B'}, {id: 2, name: 'C'}]; // 原始內容
let array2; // 賦值
let array3; // 傳址複製
let array4; // 傳值複製

array2 = array1;
array3 = array1.slice();
array4 = JSON.parse(JSON.stringify(array1));

console.log(array1); // [ { id: 0, name: 'A' }, { id: 1, name: 'B' }, { id: 2, name: 'C' } ]
console.log(array2); // [ { id: 0, name: 'A' }, { id: 1, name: 'B' }, { id: 2, name: 'C' } ]
console.log(array3); // [ { id: 0, name: 'A' }, { id: 1, name: 'B' }, { id: 2, name: 'C' } ]
console.log(array4); // [ { id: 0, name: 'A' }, { id: 1, name: 'B' }, { id: 2, name: 'C' } ]

array1[0].name = 'X';
console.log(array1); // [ { id: 0, name: 'X' }, { id: 1, name: 'B' }, { id: 2, name: 'C' } ]
console.log(array2); // [ { id: 0, name: 'X' }, { id: 1, name: 'B' }, { id: 2, name: 'C' } ]
console.log(array3); // [ { id: 0, name: 'X' }, { id: 1, name: 'B' }, { id: 2, name: 'C' } ]
console.log(array4); // [ { id: 0, name: 'A' }, { id: 1, name: 'B' }, { id: 2, name: 'C' } ]

array2[1].name = 'Y';
console.log(array1); // [ { id: 0, name: 'X' }, { id: 1, name: 'Y' }, { id: 2, name: 'C' } ]
console.log(array2); // [ { id: 0, name: 'X' }, { id: 1, name: 'Y' }, { id: 2, name: 'C' } ]
console.log(array3); // [ { id: 0, name: 'X' }, { id: 1, name: 'Y' }, { id: 2, name: 'C' } ]
console.log(array4); // [ { id: 0, name: 'A' }, { id: 1, name: 'B' }, { id: 2, name: 'C' } ]
    

我們使用 JSON 序列化和反序列化的方式將物件完整的複製,可以發現資料就不會再被其他陣列影響到了!

反轉陣列 reverse

    
let array1 = [1, 2, 3, 4];
console.log(array1); // [1, 2, 3, 4]
array1.reverse();
console.log(array1); // [4, 3, 2, 1]
    

陣列串接 concat

將兩陣列串接,產生第三個陣列
    
let array1 = [1, 2, 3];
let array2 = [4, 5, 6];
let array3 = array1.concat(array2);

console.log(array1); // [1, 2, 3]
console.log(array2); // [4, 5, 6]
console.log(array3); // [1, 2, 3, 4, 5, 6]
    

展開運算子

將 Array 展開,放入其他 Array 中
    
let array1 = [1, 2, 3];
let array2 = [4, 5, 6];

let array3 = [...array1, 4, 5, 6]
let array4 = [...array1, ...array2]

console.log(array3); // (6) [1, 2, 3, 4, 5, 6]
console.log(array4); // (6) [1, 2, 3, 4, 5, 6]

// 如果不使用展開運算子的結果
console.log([array1, array2]); // (2) [Array(3) [1, 2, 3], Array(3) [4, 5, 6]]

    

陣列內容串接 join

將陣列內容以指定符號串接,輸出 string 類型的內容,省略參數時使用逗號(,)連接
    
let array1 = [1, 2, 3, 4, 5];
console.log(array1); // [1, 2, 3, 4, 5]

let str = array1.join();
console.log(str); // 1,2,3,4,5
console.log(typeof str); // string

let str2 = array1.join('-');
console.log(str2); // 1-2-3-4-5
console.log(typeof str2); // string

    

參考資料


mdn - Array
mdn - Array.length
mdn - Handling text — strings in JavaScript
mdn - Array.prototype.forEach()
stackoverflow - change values in array when doing foreach
stackoverflow - What is the difference between a deep copy and a shallow copy?
mdn - Shallow copy
mdn - Assignment (=)
mdn - Spread syntax (...)

留言