JavaScript 數組方法完全指南
前言
數組是 JavaScript 中最常用的數據結構之一,它提供了豐富的內置方法來處理數據。掌握這些方法不僅能提高開發效率,還能讓代碼更加簡潔優雅。本文將全面介紹 JavaScript 數組的各種方法,幫助你從入門到精通。
目錄
- 數組基礎
- 創建數組
- 數組方法分類
- 增刪改查方法
- 遍歷方法
- 查找方法
- 轉換方法
- 排序和反轉
- 判斷方法
- 歸約方法
- ES6+ 新方法
- 方法對比與選擇
- 最佳實踐
- 總結
數組基礎
在 JavaScript 中,數組是一種特殊的對象,用於存儲有序的數據集合。數組具有以下特點:
- 可調整大小:數組長度可以動態變化
- 可包含不同類型:同一個數組可以存儲數字、字符串、對象等
- 索引從 0 開始:第一個元素的索引是 0
- 淺拷貝:數組複製操作創建的是淺拷貝
// 數組示例
const fruits = ['apple', 'banana', 'orange'];
console.log(fruits[0]); // 'apple'
console.log(fruits.length); // 3
創建數組
1. 字面量方式
const arr1 = []; // 空數組
const arr2 = [1, 2, 3]; // 包含元素的數組
const arr3 = ['a', 'b', 'c', 1, 2, true]; // 混合類型
2. Array 構造函數
const arr1 = new Array(); // 空數組
const arr2 = new Array(5); // 長度為 5 的稀疏數組
const arr3 = new Array(1, 2, 3); // [1, 2, 3]
3. Array.of()
const arr1 = Array.of(7); // [7]
const arr2 = Array.of(1, 2, 3); // [1, 2, 3]
// 與 new Array(7) 不同,Array.of(7) 創建的是包含一個元素 7 的數組
4. Array.from()
// 從類數組對象創建
const arr1 = Array.from('hello'); // ['h', 'e', 'l', 'l', 'o']
const arr2 = Array.from({ length: 5 }, (_, i) => i); // [0, 1, 2, 3, 4]
// 從 Set/Map 創建
const set = new Set([1, 2, 3]);
const arr3 = Array.from(set); // [1, 2, 3]
數組方法分類
增刪改查方法
push() - 在末尾添加元素
const fruits = ['apple', 'banana'];
fruits.push('orange'); // 返回新長度 3
console.log(fruits); // ['apple', 'banana', 'orange']
// 可以一次添加多個元素
fruits.push('grape', 'mango');
console.log(fruits); // ['apple', 'banana', 'orange', 'grape', 'mango']
特點:
- 修改原數組
- 返回數組的新長度
- 可以添加多個元素
pop() - 移除並返回最後一個元素
const fruits = ['apple', 'banana', 'orange'];
const last = fruits.pop(); // 'orange'
console.log(fruits); // ['apple', 'banana']
console.log(last); // 'orange'
特點:
- 修改原數組
- 返回被移除的元素
- 如果數組為空,返回
undefined
unshift() - 在開頭添加元素
const fruits = ['banana', 'orange'];
fruits.unshift('apple'); // 返回新長度 3
console.log(fruits); // ['apple', 'banana', 'orange']
特點:
- 修改原數組
- 返回數組的新長度
- 性能比
push()低(需要移動所有元素)
shift() - 移除並返回第一個元素
const fruits = ['apple', 'banana', 'orange'];
const first = fruits.shift(); // 'apple'
console.log(fruits); // ['banana', 'orange']
特點:
- 修改原數組
- 返回被移除的元素
- 性能比
pop()低
splice() - 刪除、插入或替換元素
const fruits = ['apple', 'banana', 'orange', 'grape'];
// 刪除元素:從索引 1 開始刪除 2 個元素
const removed = fruits.splice(1, 2);
console.log(fruits); // ['apple', 'grape']
console.log(removed); // ['banana', 'orange']
// 插入元素:從索引 1 開始,刪除 0 個,插入新元素
fruits.splice(1, 0, 'mango', 'peach');
console.log(fruits); // ['apple', 'mango', 'peach', 'grape']
// 替換元素:從索引 1 開始,刪除 1 個,插入新元素
fruits.splice(1, 1, 'banana');
console.log(fruits); // ['apple', 'banana', 'peach', 'grape']
語法:array.splice(start, deleteCount, ...items)
特點:
- 修改原數組
- 返回被刪除元素的數組
- 功能強大,可以刪除、插入、替換
slice() - 提取數組片段(不修改原數組)
const fruits = ['apple', 'banana', 'orange', 'grape', 'mango'];
// 提取從索引 1 到 3(不包括 3)的元素
const sliced = fruits.slice(1, 3);
console.log(sliced); // ['banana', 'orange']
console.log(fruits); // 原數組不變
// 只提供開始索引,提取到末尾
const fromIndex2 = fruits.slice(2);
console.log(fromIndex2); // ['orange', 'grape', 'mango']
// 負數索引:從末尾開始計算
const lastTwo = fruits.slice(-2);
console.log(lastTwo); // ['grape', 'mango']
// 複製整個數組
const copy = fruits.slice();
特點:
- 不修改原數組
- 返回新數組
- 常用於數組複製
遍歷方法
forEach() - 遍歷數組
const numbers = [1, 2, 3, 4, 5];
numbers.forEach((value, index, array) => {
console.log(`索引 ${index}: 值 ${value}`);
});
// 輸出:
// 索引 0: 值 1
// 索引 1: 值 2
// 索引 2: 值 3
// 索引 3: 值 4
// 索引 4: 值 5
// 修改原數組(不推薦,但可以)
const doubled = [];
numbers.forEach(num => {
doubled.push(num * 2);
});
console.log(doubled); // [2, 4, 6, 8, 10]
特點:
- 不返回新數組(返回
undefined) - 無法使用
break或continue(可以使用return跳過當前迭代) - 不會跳過空槽(稀疏數組)
map() - 映射數組(返回新數組)
const numbers = [1, 2, 3, 4, 5];
// 將每個數字乘以 2
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
console.log(numbers); // 原數組不變 [1, 2, 3, 4, 5]
// 提取對象屬性
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
{ name: 'Charlie', age: 35 }
];
const names = users.map(user => user.name);
console.log(names); // ['Alice', 'Bob', 'Charlie']
特點:
- 不修改原數組
- 返回新數組
- 新數組長度與原數組相同
- 最常用的數組方法之一
filter() - 過濾數組
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 篩選偶數
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4, 6, 8, 10]
// 篩選對象
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 17 },
{ name: 'Charlie', age: 30 }
];
const adults = users.filter(user => user.age >= 18);
console.log(adults); // [{ name: 'Alice', age: 25 }, { name: 'Charlie', age: 30 }]
特點:
- 不修改原數組
- 返回新數組
- 新數組長度可能小於原數組
- 回調函數返回
true的元素會被保留
reduce() - 歸約數組
const numbers = [1, 2, 3, 4, 5];
// 求和
const sum = numbers.reduce((accumulator, currentValue) => {
return accumulator + currentValue;
}, 0);
console.log(sum); // 15
// 簡化寫法
const sum2 = numbers.reduce((acc, cur) => acc + cur, 0);
// 求最大值
const max = numbers.reduce((acc, cur) => Math.max(acc, cur));
// 數組轉對象
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
];
const userMap = users.reduce((acc, user) => {
acc[user.id] = user.name;
return acc;
}, {});
console.log(userMap); // { 1: 'Alice', 2: 'Bob', 3: 'Charlie' }
// 數組扁平化
const nested = [[1, 2], [3, 4], [5, 6]];
const flat = nested.reduce((acc, cur) => acc.concat(cur), []);
console.log(flat); // [1, 2, 3, 4, 5, 6]
特點:
- 不修改原數組
- 返回單個值
- 功能強大,可以實現很多複雜操作
- 初始值可選(建議總是提供)
reduceRight() - 從右到左歸約
const numbers = [1, 2, 3, 4];
// 從右到左計算
const result = numbers.reduceRight((acc, cur) => acc - cur);
console.log(result); // -2 (4 - 3 - 2 - 1 = -2)
// 與 reduce 對比
const result2 = numbers.reduce((acc, cur) => acc - cur);
console.log(result2); // -8 (1 - 2 - 3 - 4 = -8)
查找方法
indexOf() - 查找元素索引
const fruits = ['apple', 'banana', 'orange', 'banana'];
const index = fruits.indexOf('banana');
console.log(index); // 1
// 從指定位置開始查找
const index2 = fruits.indexOf('banana', 2);
console.log(index2); // 3
// 找不到返回 -1
const index3 = fruits.indexOf('grape');
console.log(index3); // -1
特點:
- 使用嚴格相等(===)比較
- 返回第一個匹配的索引
- 找不到返回 -1
lastIndexOf() - 從後往前查找
const fruits = ['apple', 'banana', 'orange', 'banana'];
const index = fruits.lastIndexOf('banana');
console.log(index); // 3
includes() - 判斷是否包含元素
const fruits = ['apple', 'banana', 'orange'];
console.log(fruits.includes('banana')); // true
console.log(fruits.includes('grape')); // false
// 從指定位置開始查找
console.log(fruits.includes('apple', 1)); // false
特點:
- 返回布爾值
- ES6 新增方法
- 比
indexOf() !== -1更語義化
find() - 查找第一個滿足條件的元素
const users = [
{ id: 1, name: 'Alice', age: 25 },
{ id: 2, name: 'Bob', age: 17 },
{ id: 3, name: 'Charlie', age: 30 }
];
// 查找第一個年齡大於等於 18 的用户
const adult = users.find(user => user.age >= 18);
console.log(adult); // { id: 1, name: 'Alice', age: 25 }
// 找不到返回 undefined
const senior = users.find(user => user.age > 100);
console.log(senior); // undefined
特點:
- 不修改原數組
- 返回第一個匹配的元素
- 找不到返回
undefined
findIndex() - 查找第一個滿足條件的元素索引
const users = [
{ id: 1, name: 'Alice', age: 25 },
{ id: 2, name: 'Bob', age: 17 },
{ id: 3, name: 'Charlie', age: 30 }
];
const index = users.findIndex(user => user.age >= 18);
console.log(index); // 0
// 找不到返回 -1
const notFound = users.findIndex(user => user.age > 100);
console.log(notFound); // -1
findLast() - 查找最後一個滿足條件的元素(ES2023)
const numbers = [1, 2, 3, 4, 5, 6];
const lastEven = numbers.findLast(num => num % 2 === 0);
console.log(lastEven); // 6
findLastIndex() - 查找最後一個滿足條件的元素索引(ES2023)
const numbers = [1, 2, 3, 4, 5, 6];
const lastEvenIndex = numbers.findLastIndex(num => num % 2 === 0);
console.log(lastEvenIndex); // 5
轉換方法
join() - 數組轉字符串
const fruits = ['apple', 'banana', 'orange'];
console.log(fruits.join()); // 'apple,banana,orange'
console.log(fruits.join('')); // 'applebananaorange'
console.log(fruits.join(' - ')); // 'apple - banana - orange'
特點:
- 不修改原數組
- 默認使用逗號分隔
- 空數組返回空字符串
toString() - 轉換為字符串
const fruits = ['apple', 'banana', 'orange'];
console.log(fruits.toString()); // 'apple,banana,orange'
// 等同於 fruits.join()
toLocaleString() - 本地化字符串
const numbers = [1234.56, 7890.12];
console.log(numbers.toLocaleString('zh-CN')); // '1,234.56,7,890.12'
flat() - 扁平化數組
const nested = [1, [2, 3], [4, [5, 6]]];
// 默認只扁平化一層
console.log(nested.flat()); // [1, 2, 3, 4, [5, 6]]
// 指定深度
console.log(nested.flat(2)); // [1, 2, 3, 4, 5, 6]
// 扁平化所有層級
console.log(nested.flat(Infinity)); // [1, 2, 3, 4, 5, 6]
特點:
- 不修改原數組
- ES2019 新增
- 可以指定扁平化深度
flatMap() - 映射後扁平化
const numbers = [1, 2, 3];
// 相當於 map().flat()
const result = numbers.flatMap(x => [x, x * 2]);
console.log(result); // [1, 2, 2, 4, 3, 6]
// 與 map().flat() 對比
const result2 = numbers.map(x => [x, x * 2]).flat();
console.log(result2); // [1, 2, 2, 4, 3, 6]
特點:
- 不修改原數組
- ES2019 新增
- 性能比
map().flat()更好
排序和反轉
sort() - 排序
const numbers = [3, 1, 4, 1, 5, 9, 2, 6];
// 默認按字符串排序(不推薦)
numbers.sort();
console.log(numbers); // [1, 1, 2, 3, 4, 5, 6, 9]
// 數字排序(升序)
const numbers2 = [3, 1, 4, 1, 5, 9, 2, 6];
numbers2.sort((a, b) => a - b);
console.log(numbers2); // [1, 1, 2, 3, 4, 5, 6, 9]
// 數字排序(降序)
const numbers3 = [3, 1, 4, 1, 5, 9, 2, 6];
numbers3.sort((a, b) => b - a);
console.log(numbers3); // [9, 6, 5, 4, 3, 2, 1, 1]
// 對象排序
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 17 },
{ name: 'Charlie', age: 30 }
];
users.sort((a, b) => a.age - b.age);
console.log(users);
// [{ name: 'Bob', age: 17 }, { name: 'Alice', age: 25 }, { name: 'Charlie', age: 30 }]
特點:
- 修改原數組
- 默認按字符串 Unicode 碼點排序
- 需要提供比較函數進行數字排序
reverse() - 反轉數組
const fruits = ['apple', 'banana', 'orange'];
fruits.reverse();
console.log(fruits); // ['orange', 'banana', 'apple']
特點:
- 修改原數組
- 返回反轉後的數組(原數組的引用)
toSorted() - 排序(不修改原數組,ES2023)
const numbers = [3, 1, 4, 1, 5];
const sorted = numbers.toSorted((a, b) => a - b);
console.log(sorted); // [1, 1, 3, 4, 5]
console.log(numbers); // [3, 1, 4, 1, 5] 原數組不變
toReversed() - 反轉(不修改原數組,ES2023)
const fruits = ['apple', 'banana', 'orange'];
const reversed = fruits.toReversed();
console.log(reversed); // ['orange', 'banana', 'apple']
console.log(fruits); // ['apple', 'banana', 'orange'] 原數組不變
判斷方法
every() - 判斷是否所有元素都滿足條件
const numbers = [2, 4, 6, 8, 10];
// 判斷是否都是偶數
const allEven = numbers.every(num => num % 2 === 0);
console.log(allEven); // true
const numbers2 = [2, 4, 6, 7, 8];
const allEven2 = numbers2.every(num => num % 2 === 0);
console.log(allEven2); // false
特點:
- 不修改原數組
- 返回布爾值
- 空數組返回
true(空真值)
some() - 判斷是否有元素滿足條件
const numbers = [1, 3, 5, 7, 8];
// 判斷是否有偶數
const hasEven = numbers.some(num => num % 2 === 0);
console.log(hasEven); // true
const numbers2 = [1, 3, 5, 7, 9];
const hasEven2 = numbers2.some(num => num % 2 === 0);
console.log(hasEven2); // false
特點:
- 不修改原數組
- 返回布爾值
- 空數組返回
false
歸約方法
reduce() 和 reduceRight()
已在前面詳細介紹,這裏不再重複。
ES6+ 新方法
Array.from() - 從類數組創建數組
// 從字符串
Array.from('hello'); // ['h', 'e', 'l', 'l', 'o']
// 從 Set
Array.from(new Set([1, 2, 3])); // [1, 2, 3]
// 從對象(需要 length 屬性)
Array.from({ length: 5 }, (_, i) => i); // [0, 1, 2, 3, 4]
// 從 NodeList
const divs = document.querySelectorAll('div');
const divArray = Array.from(divs);
Array.of() - 創建數組
Array.of(7); // [7]
Array.of(1, 2, 3); // [1, 2, 3]
// 與 new Array(7) 不同
fill() - 填充數組
// 填充整個數組
const arr1 = new Array(5).fill(0);
console.log(arr1); // [0, 0, 0, 0, 0]
// 從指定位置填充
const arr2 = [1, 2, 3, 4, 5];
arr2.fill(0, 2, 4);
console.log(arr2); // [1, 2, 0, 0, 5]
特點:
- 修改原數組
- 可以指定填充的起始和結束位置
copyWithin() - 複製數組元素
const arr = [1, 2, 3, 4, 5];
// 將索引 0-2 的元素複製到索引 3 開始的位置
arr.copyWithin(3, 0, 3);
console.log(arr); // [1, 2, 3, 1, 2]
特點:
- 修改原數組
- 使用場景較少
entries() - 返回鍵值對迭代器
const fruits = ['apple', 'banana', 'orange'];
for (const [index, value] of fruits.entries()) {
console.log(index, value);
}
// 0 'apple'
// 1 'banana'
// 2 'orange'
keys() - 返回鍵迭代器
const fruits = ['apple', 'banana', 'orange'];
for (const index of fruits.keys()) {
console.log(index);
}
// 0
// 1
// 2
values() - 返回值迭代器
const fruits = ['apple', 'banana', 'orange'];
for (const value of fruits.values()) {
console.log(value);
}
// 'apple'
// 'banana'
// 'orange'
at() - 按索引訪問(支持負數,ES2022)
const fruits = ['apple', 'banana', 'orange'];
console.log(fruits.at(0)); // 'apple'
console.log(fruits.at(-1)); // 'orange'(最後一個)
console.log(fruits.at(-2)); // 'banana'(倒數第二個)
特點:
- 不修改原數組
- 支持負數索引
- 比
fruits[fruits.length - 1]更優雅
with() - 修改指定索引的值(不修改原數組,ES2023)
const fruits = ['apple', 'banana', 'orange'];
const newFruits = fruits.with(1, 'grape');
console.log(newFruits); // ['apple', 'grape', 'orange']
console.log(fruits); // ['apple', 'banana', 'orange'] 原數組不變
toSpliced() - 刪除、插入或替換(不修改原數組,ES2023)
const fruits = ['apple', 'banana', 'orange'];
const newFruits = fruits.toSpliced(1, 1, 'grape', 'mango');
console.log(newFruits); // ['apple', 'grape', 'mango', 'orange']
console.log(fruits); // ['apple', 'banana', 'orange'] 原數組不變
方法對比與選擇
修改原數組 vs 不修改原數組
|
修改原數組
|
不修改原數組
|
|
push, pop, shift, unshift
|
map, filter, slice
|
|
splice, sort, reverse
|
concat, join, toString
|
|
fill, copyWithin
|
flat, flatMap
|
|
toSorted, toReversed, with, toSpliced
|
選擇建議:
- 優先使用不修改原數組的方法(函數式編程)
- 需要修改原數組時,明確知道副作用
查找方法對比
|
方法
|
返回值
|
使用場景
|
|
indexOf
|
索引或 -1
|
簡單值查找
|
|
includes
|
布爾值
|
判斷是否存在
|
|
find
|
元素或 undefined
|
對象查找
|
|
findIndex
|
索引或 -1
|
對象查找索引
|
|
some
|
布爾值
|
判斷是否有滿足條件的元素
|
遍歷方法對比
|
方法
|
返回值
|
是否可中斷
|
使用場景
|
|
forEach
|
undefined
|
否
|
簡單遍歷
|
|
map
|
新數組
|
否
|
轉換數組
|
|
filter
|
新數組
|
否
|
過濾數組
|
|
for…of
|
-
|
是
|
需要中斷的遍歷
|
|
reduce
|
單個值
|
否
|
歸約計算
|
最佳實踐
1. 鏈式調用
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 鏈式調用多個方法
const result = numbers
.filter(num => num % 2 === 0) // [2, 4, 6, 8, 10]
.map(num => num * 2) // [4, 8, 12, 16, 20]
.reduce((sum, num) => sum + num, 0); // 60
console.log(result); // 60
2. 避免在遍歷中修改數組
// ❌ 不推薦:在 forEach 中修改數組長度
const numbers = [1, 2, 3, 4, 5];
numbers.forEach((num, index) => {
if (num % 2 === 0) {
numbers.splice(index, 1); // 危險!會跳過元素
}
});
// ✅ 推薦:使用 filter
const numbers2 = [1, 2, 3, 4, 5];
const odds = numbers2.filter(num => num % 2 !== 0);
3. 使用解構賦值
// 交換變量
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a, b); // 2, 1
// 獲取第一個和剩餘元素
const [first, ...rest] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(rest); // [2, 3, 4, 5]
4. 性能優化
// ❌ 不推薦:多次遍歷
const numbers = [1, 2, 3, 4, 5];
const evens = numbers.filter(n => n % 2 === 0);
const doubled = evens.map(n => n * 2);
const sum = doubled.reduce((a, b) => a + b, 0);
// ✅ 推薦:單次遍歷
const sum2 = numbers
.filter(n => n % 2 === 0)
.map(n => n * 2)
.reduce((a, b) => a + b, 0);
// ✅ 更推薦:使用 reduce 一次完成
const sum3 = numbers.reduce((acc, n) => {
if (n % 2 === 0) {
return acc + n * 2;
}
return acc;
}, 0);
5. 處理空值和邊界情況
// 安全的數組操作
function safeArrayOperation(arr) {
if (!Array.isArray(arr) || arr.length === 0) {
return [];
}
return arr.map(item => item * 2);
}
// 使用可選鏈和空值合併
const result = array?.map(x => x * 2) ?? [];
6. 數組去重
// 方法 1:使用 Set
const unique1 = [...new Set([1, 2, 2, 3, 3, 3])];
console.log(unique1); // [1, 2, 3]
// 方法 2:使用 filter + indexOf
const unique2 = [1, 2, 2, 3, 3, 3].filter((item, index, arr) =>
arr.indexOf(item) === index
);
// 方法 3:對象數組去重
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 1, name: 'Alice' }
];
const uniqueUsers = users.filter((user, index, arr) =>
arr.findIndex(u => u.id === user.id) === index
);
console.log(uniqueUsers); // [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
// 方法 4:使用 Map(性能更好)
const uniqueUsers2 = Array.from(
new Map(users.map(user => [user.id, user])).values()
);
7. 數組分組
// 使用 reduce 實現分組
const users = [
{ name: 'Alice', age: 25, city: 'Beijing' },
{ name: 'Bob', age: 30, city: 'Shanghai' },
{ name: 'Charlie', age: 25, city: 'Beijing' },
{ name: 'David', age: 30, city: 'Shanghai' }
];
// 按城市分組
const groupedByCity = users.reduce((acc, user) => {
const city = user.city;
if (!acc[city]) {
acc[city] = [];
}
acc[city].push(user);
return acc;
}, {});
console.log(groupedByCity);
// {
// Beijing: [{ name: 'Alice', ... }, { name: 'Charlie', ... }],
// Shanghai: [{ name: 'Bob', ... }, { name: 'David', ... }]
// }
// 按年齡分組
const groupedByAge = users.reduce((acc, user) => {
const age = user.age;
acc[age] = acc[age] || [];
acc[age].push(user);
return acc;
}, {});
8. 數組分塊
// 將數組分割成指定大小的塊
function chunk(array, size) {
const chunks = [];
for (let i = 0; i < array.length; i += size) {
chunks.push(array.slice(i, i + size));
}
return chunks;
}
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(chunk(numbers, 3)); // [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// 使用 reduce 實現
const chunk2 = (array, size) =>
array.reduce((acc, _, i) => {
if (i % size === 0) {
acc.push(array.slice(i, i + size));
}
return acc;
}, []);
9. 數組合並
// concat() - 合併數組(不修改原數組)
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const arr3 = arr1.concat(arr2);
console.log(arr3); // [1, 2, 3, 4, 5, 6]
console.log(arr1); // [1, 2, 3] 原數組不變
// 使用擴展運算符(推薦)
const arr4 = [...arr1, ...arr2];
console.log(arr4); // [1, 2, 3, 4, 5, 6]
// 合併多個數組
const arr5 = [7, 8, 9];
const merged = [...arr1, ...arr2, ...arr5];
console.log(merged); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
10. 數組交集、並集、差集
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [4, 5, 6, 7, 8];
// 交集:兩個數組都有的元素
const intersection = arr1.filter(item => arr2.includes(item));
console.log(intersection); // [4, 5]
// 並集:兩個數組的所有元素(去重)
const union = [...new Set([...arr1, ...arr2])];
console.log(union); // [1, 2, 3, 4, 5, 6, 7, 8]
// 差集:arr1 有但 arr2 沒有的元素
const difference = arr1.filter(item => !arr2.includes(item));
console.log(difference); // [1, 2, 3]
// 對稱差集:兩個數組獨有的元素
const symmetricDifference = [
...arr1.filter(item => !arr2.includes(item)),
...arr2.filter(item => !arr1.includes(item))
];
console.log(symmetricDifference); // [1, 2, 3, 6, 7, 8]
11. 數組隨機打亂
// Fisher-Yates 洗牌算法
function shuffle(array) {
const arr = [...array]; // 避免修改原數組
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}
const numbers = [1, 2, 3, 4, 5];
console.log(shuffle(numbers)); // 隨機順序
console.log(numbers); // [1, 2, 3, 4, 5] 原數組不變
12. 數組扁平化(多種方法)
const nested = [1, [2, 3], [4, [5, 6]]];
// 方法 1:使用 flat()
const flat1 = nested.flat(Infinity);
// 方法 2:使用 reduce + concat
const flat2 = nested.reduce((acc, cur) =>
acc.concat(Array.isArray(cur) ? flat2(cur) : cur), []
);
// 方法 3:使用擴展運算符(僅一層)
const flat3 = [].concat(...nested);
// 方法 4:使用 toString()(僅適用於數字)
const numbers = [[1, 2], [3, 4]];
const flat4 = numbers.toString().split(',').map(Number);
13. 數組統計
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 求和
const sum = numbers.reduce((a, b) => a + b, 0);
// 平均值
const average = numbers.reduce((a, b) => a + b, 0) / numbers.length;
// 最大值和最小值
const max = Math.max(...numbers);
const min = Math.min(...numbers);
// 使用 reduce 求最大值和最小值
const max2 = numbers.reduce((a, b) => Math.max(a, b));
const min2 = numbers.reduce((a, b) => Math.min(a, b));
// 統計元素出現次數
const count = numbers.reduce((acc, num) => {
acc[num] = (acc[num] || 0) + 1;
return acc;
}, {});
14. 數組轉對象
// 鍵值對數組轉對象
const entries = [['name', 'Alice'], ['age', 25], ['city', 'Beijing']];
const obj = Object.fromEntries(entries);
console.log(obj); // { name: 'Alice', age: 25, city: 'Beijing' }
// 對象數組轉對象(以某個屬性為鍵)
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
];
const userMap = users.reduce((acc, user) => {
acc[user.id] = user;
return acc;
}, {});
console.log(userMap);
// {
// 1: { id: 1, name: 'Alice' },
// 2: { id: 2, name: 'Bob' },
// 3: { id: 3, name: 'Charlie' }
// }
15. 數組補全方法補充
concat() - 合併數組
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
// 合併數組
const merged = arr1.concat(arr2);
console.log(merged); // [1, 2, 3, 4, 5, 6]
// 可以合併多個數組
const arr3 = [7, 8, 9];
const all = arr1.concat(arr2, arr3);
console.log(all); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
// 也可以合併值
const withValues = arr1.concat(4, 5, [6, 7]);
console.log(withValues); // [1, 2, 3, 4, 5, 6, 7]
特點:
- 不修改原數組
- 返回新數組
- 可以合併多個數組或值
常見應用場景
場景 1:數據處理和轉換
// 從 API 獲取數據後處理
const apiData = [
{ id: 1, name: 'Alice', status: 'active' },
{ id: 2, name: 'Bob', status: 'inactive' },
{ id: 3, name: 'Charlie', status: 'active' }
];
// 提取活躍用户的名字
const activeUsers = apiData
.filter(user => user.status === 'active')
.map(user => user.name);
console.log(activeUsers); // ['Alice', 'Charlie']
場景 2:表單驗證
// 驗證表單字段
const formFields = [
{ name: 'username', value: 'alice', required: true },
{ name: 'email', value: '', required: true },
{ name: 'age', value: '25', required: false }
];
// 檢查必填字段是否都已填寫
const isValid = formFields
.filter(field => field.required)
.every(field => field.value.trim() !== '');
console.log(isValid); // false(email 為空)
場景 3:購物車計算
const cart = [
{ id: 1, name: '商品A', price: 100, quantity: 2 },
{ id: 2, name: '商品B', price: 200, quantity: 1 },
{ id: 3, name: '商品C', price: 50, quantity: 3 }
];
// 計算總價
const total = cart.reduce((sum, item) =>
sum + item.price * item.quantity, 0
);
console.log(total); // 450
// 計算商品總數
const totalQuantity = cart.reduce((sum, item) =>
sum + item.quantity, 0
);
console.log(totalQuantity); // 6
場景 4:數據篩選和搜索
const products = [
{ id: 1, name: 'iPhone', category: '手機', price: 5000 },
{ id: 2, name: 'iPad', category: '平板', price: 3000 },
{ id: 3, name: 'MacBook', category: '電腦', price: 10000 }
];
// 按價格篩選
const affordable = products.filter(p => p.price < 5000);
console.log(affordable); // [{ id: 2, name: 'iPad', ... }]
// 搜索功能
function searchProducts(products, keyword) {
return products.filter(product =>
product.name.toLowerCase().includes(keyword.toLowerCase())
);
}
console.log(searchProducts(products, 'phone'));
// [{ id: 1, name: 'iPhone', ... }]
場景 5:數據分組和聚合
const orders = [
{ date: '2024-01-01', amount: 100, category: '電子產品' },
{ date: '2024-01-01', amount: 50, category: '食品' },
{ date: '2024-01-02', amount: 200, category: '電子產品' },
{ date: '2024-01-02', amount: 30, category: '食品' }
];
// 按日期分組並計算總金額
const dailyTotal = orders.reduce((acc, order) => {
const date = order.date;
acc[date] = (acc[date] || 0) + order.amount;
return acc;
}, {});
console.log(dailyTotal);
// { '2024-01-01': 150, '2024-01-02': 230 }
// 按類別分組
const categoryTotal = orders.reduce((acc, order) => {
const category = order.category;
acc[category] = (acc[category] || 0) + order.amount;
return acc;
}, {});
console.log(categoryTotal);
// { '電子產品': 300, '食品': 80 }
性能注意事項
1. 方法選擇
// ❌ 性能較差:多次遍歷
const numbers = [1, 2, 3, 4, 5];
const evens = numbers.filter(n => n % 2 === 0);
const doubled = evens.map(n => n * 2);
// ✅ 性能較好:單次遍歷
const doubled2 = numbers.reduce((acc, n) => {
if (n % 2 === 0) {
acc.push(n * 2);
}
return acc;
}, []);
2. 大數組處理
// 對於大數組,考慮使用 for 循環
const largeArray = new Array(1000000).fill(0).map((_, i) => i);
// ❌ 可能較慢
const result1 = largeArray.map(x => x * 2);
// ✅ 可能更快
const result2 = [];
for (let i = 0; i < largeArray.length; i++) {
result2[i] = largeArray[i] * 2;
}
3. 提前退出
// 使用 some() 或 find() 可以提前退出
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// ✅ 找到第一個偶數就停止
const firstEven = numbers.find(n => n % 2 === 0);
// ❌ 會遍歷所有元素
const firstEven2 = numbers.filter(n => n % 2 === 0)[0];
總結
JavaScript 數組提供了豐富而強大的方法,掌握這些方法可以大大提高開發效率。以下是關鍵要點:
核心方法分類
- 增刪改查:
push,pop,shift,unshift,splice,slice - 遍歷轉換:
forEach,map,filter,reduce - 查找判斷:
find,findIndex,includes,some,every - 排序反轉:
sort,reverse,toSorted,toReversed - 轉換方法:
join,flat,flatMap,concat
選擇原則
- 優先使用不修改原數組的方法(函數式編程)
- 根據需求選擇合適的方法(查找用
find,判斷用some/every) - 鏈式調用提高代碼可讀性
- 注意性能,大數組考慮使用傳統循環
最佳實踐
- ✅ 使用
map轉換數組 - ✅ 使用
filter過濾數組 - ✅ 使用
reduce進行復雜計算 - ✅ 使用
find查找元素 - ✅ 使用
includes判斷存在性 - ❌ 避免在遍歷中修改數組
- ❌ 避免不必要的多次遍歷
學習建議
- 理解每個方法的返回值:是否修改原數組、返回什麼類型
- 掌握鏈式調用:組合使用多個方法
- 熟悉常見場景:去重、分組、統計等
- 注意邊界情況:空數組、undefined、null 等
- 關注性能:大數組時選擇合適的方法
希望這篇指南能幫助你更好地掌握 JavaScript 數組方法,在實際開發中靈活運用!