目錄:
陣列中可以儲存不同型別的元素,也不會影響(變更)元素的型別:
若設定的長度大於陣列本身的長度,則會填入空元素
註:長度需要介於 0 ~ 4294967295 (2 的 32 次方減 1)之間
不然會拋出: RangeError: Invalid array length
如果已經有三個元素,放入第四個會和 push 一樣加在最後面,如果沒有第五個元素依然可以直接放入第六個,此時第五個會和上面設定陣列長度時一樣自動塞入空元素
在第四個位置放入 'E' 後陣列長度變成 4,在第六個位置放入 'F' 後陣列長度變成 6 ,第五個位置自動放入 undefined,在使用 forEach 歷遍時不會被輸出。專有名詞叫做不可迭代(non-iterable)的空槽(empty slot)
index 為第幾個
array 則是陣列本身
可以省略 array 或是 index 和 array 一起省略
奇怪了?明明在第 3 行修改,在第 4 行輸出中也有看到有變更,可是跑完迴圈後怎麼都沒有變?
其實我們可以把 forEach 的第一個參數 element 理解為當前資料的副本,只會在當前的區塊有效,要變更內容也很簡單,forEach 的第二個參數是該元素在此 array 中的索引(第幾個),所以我們就可以很簡單的直接操作原本的 array 了:
在絕大多數的程式語言中,複製都有兩種,分別是複製位址(傳址(reference), 淺拷貝, Shallow Copy) 和複製內容(傳值(value), 深拷貝, Deep Copy),其實只有複製第一層,裡面的內容還是引用原本的資料。有什麼區別呢?先看看下面的範例:
可以發現這次居然連使用 slice 複製的內容也會被變更!
所以在實作的時候要非常小心,避免改到不該改的資料。
雖然不是本篇的內容,但是還是示範一下其中一種傳值複製的方式,一樣使用剛剛的例子:
我們使用 JSON 序列化和反序列化的方式將物件完整的複製,可以發現資料就不會再被其他陣列影響到了!
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 (...)
- 宣告/建立陣列
- 陣列長度 length
- 取得指定位置的元素
- 取得指定元素的位置
- 陣列增加元素
- 移除元素
- 歷遍
- 複製陣列
- 反轉陣列 reverse
- 陣列串接 concat
- 展開運算子 ...
- 陣列內容串接 join
- 參考資料
宣告/建立陣列
// 建立陣列
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 (...)
留言
張貼留言
如果有任何問題、建議、想說的話或文章題目推薦,都歡迎留言或來信: a@ruyut.com