JavaScript之數組方法詳解
- 一、數組的創建與基礎特性
- 1.1 數組的創建方式
- 1.2 數組的核心特性
- 二、修改原數組的方法
- 2.1 添加/刪除元素
- 2.1.1 `push()`:尾部添加元素
- 2.1.2 `pop()`:尾部刪除元素
- 2.1.3 `unshift()`:頭部添加元素
- 2.1.4 `shift()`:頭部刪除元素
- 2.1.5 `splice()`:指定位置添加/刪除
- 2.2 排序與反轉
- 2.2.1 `reverse()`:反轉數組
- 2.2.2 `sort()`:排序數組
- 三、不修改原數組的方法
- 3.1 數組截取與合并
- 3.1.1 `slice()`:截取子數組
- 3.1.2 `concat()`:合并數組
- 3.2 元素查找
- 3.2.1 `indexOf()`與`lastIndexOf()`:查找索引
- 3.2.2 `includes()`:判斷元素是否存在
- 3.2.3 `find()`與`findIndex()`:查找符合條件的元素
- 3.3 數組轉換
- 3.3.1 `join()`:數組轉字符串
- 3.3.2 `toString()`:數組轉字符串(簡化版)
- 四、遍歷與迭代方法
- 4.1 基礎遍歷
- 4.1.1 `forEach()`:遍歷元素
- 4.2 映射與過濾
- 4.2.1 `map()`:映射新數組
- 4.2.2 `filter()`:過濾元素
- 4.3 聚合與檢測
- 4.3.1 `reduce()`:累加計算
- 4.3.2 `every()`與`some()`:條件檢測
- 五、方法對比與最佳實踐
- 5.1 方法選擇指南
- 5.2 性能與注意事項
- 六、常見問題與避坑指南
- 6.1 `sort()`的默認排序陷阱
- 6.2 `indexOf()`無法識別`NaN`
- 6.3 `map()`返回新數組的長度不變
數組是JavaScript中最常用的數據結構之一,掌握數組方法是高效處理數據的基礎,本文我將系統梳理JavaScript數組的常用方法,從基礎的添加刪除到復雜的遍歷轉換,并結合實例解析每種方法的用法、特性及適用場景,幫你徹底搞懂數組操作的核心技巧。
一、數組的創建與基礎特性
在學習方法之前,先回顧數組的創建方式,這是后續操作的基礎:
1.1 數組的創建方式
// 1. 字面量方式(最常用)
const arr1 = [1, 2, 3];// 2. 構造函數方式
const arr2 = new Array(3); // 創建長度為3的空數組([ , , ])
const arr3 = new Array(1, 2, 3); // 創建包含元素的數組([1,2,3])// 3. 靜態方法創建(ES6+)
const arr4 = Array.of(1, 2, 3); // 類似字面量,避免new Array的歧義
const arr5 = Array.from([1, 2, 3], x => x * 2); // 從類數組/可迭代對象創建并映射([2,4,6])
1.2 數組的核心特性
- 動態長度:數組長度可自動擴展(如
arr.push(4)
會增加長度) - 元素類型不限:可包含數字、字符串、對象等任意類型
- 索引從0開始:通過
arr[index]
訪問元素
二、修改原數組的方法
這類方法會直接改變原數組,操作時需注意對原數據的影響。
2.1 添加/刪除元素
2.1.1 push()
:尾部添加元素
const fruits = ['apple', 'banana'];
const newLength = fruits.push('orange', 'grape'); // 添加多個元素
console.log(fruits); // ['apple', 'banana', 'orange', 'grape']
console.log(newLength); // 4(返回新長度)
特性:接受任意數量參數,添加到數組末尾,返回新長度。
2.1.2 pop()
:尾部刪除元素
const lastFruit = fruits.pop(); // 刪除最后一個元素
console.log(fruits); // ['apple', 'banana', 'orange']
console.log(lastFruit); // 'grape'(返回被刪除的元素)
特性:無參數,刪除最后一個元素,返回被刪除元素。
2.1.3 unshift()
:頭部添加元素
const newLength = fruits.unshift('mango'); // 頭部添加
console.log(fruits); // ['mango', 'apple', 'banana', 'orange']
console.log(newLength); // 4(返回新長度)
特性:接受任意數量參數,添加到數組頭部,返回新長度(性能比push()
差,因需移動所有元素)。
2.1.4 shift()
:頭部刪除元素
const firstFruit = fruits.shift(); // 刪除第一個元素
console.log(fruits); // ['apple', 'banana', 'orange']
console.log(firstFruit); // 'mango'(返回被刪除元素)
特性:無參數,刪除第一個元素,返回被刪除元素(性能較差,同unshift()
)。
2.1.5 splice()
:指定位置添加/刪除
const numbers = [1, 2, 3, 4, 5];// 1. 刪除:splice(起始索引, 刪除數量)
const deleted = numbers.splice(2, 2); // 從索引2開始刪除2個元素
console.log(numbers); // [1, 2, 5]
console.log(deleted); // [3, 4](返回被刪除元素數組)// 2. 添加:splice(起始索引, 0, 添加元素1, ...)
numbers.splice(2, 0, 3, 4); // 從索引2開始,刪除0個,添加3和4
console.log(numbers); // [1, 2, 3, 4, 5](恢復原數組)// 3. 替換:splice(起始索引, 刪除數量, 替換元素)
numbers.splice(1, 2, 'a', 'b'); // 從索引1刪除2個,添加'a','b'
console.log(numbers); // [1, 'a', 'b', 4, 5]
特性:功能強大,可同時完成添加和刪除,返回被刪除元素數組(若未刪除則返回空數組)。
2.2 排序與反轉
2.2.1 reverse()
:反轉數組
const arr = [1, 2, 3];
arr.reverse();
console.log(arr); // [3, 2, 1]
特性:直接反轉原數組,返回反轉后的數組(與原數組引用相同)。
2.2.2 sort()
:排序數組
// 1. 默認排序(按字符串Unicode碼點,可能不符合預期)
const nums = [10, 2, 23];
nums.sort();
console.log(nums); // [10, 2, 23](錯誤排序,因'10'在'2'前)// 2. 數值升序排序(傳入比較函數)
nums.sort((a, b) => a - b);
console.log(nums); // [2, 10, 23]// 3. 數值降序排序
nums.sort((a, b) => b - a);
console.log(nums); // [23, 10, 2]// 4. 對象數組排序(按age屬性升序)
const users = [{ name: 'Bob', age: 25 },{ name: 'Alice', age: 20 }
];
users.sort((a, b) => a.age - b.age);
console.log(users); // [Alice(20), Bob(25)]
特性:默認按字符串排序,需傳入比較函數(a,b) => a - b
(升序)或(a,b) => b - a
(降序)實現數值排序;直接修改原數組,返回排序后的數組。
三、不修改原數組的方法
這類方法會返回新數組或其他結果,原數組保持不變,適合函數式編程。
3.1 數組截取與合并
3.1.1 slice()
:截取子數組
const arr = [1, 2, 3, 4, 5];// 1. slice(起始索引, 結束索引):含頭不含尾
const sub1 = arr.slice(1, 4); // 從索引1到3(不包含4)
console.log(sub1); // [2, 3, 4]// 2. 省略結束索引:截取到末尾
const sub2 = arr.slice(2); // 從索引2到末尾
console.log(sub2); // [3, 4, 5]// 3. 負數索引:從末尾開始計算
const sub3 = arr.slice(-3, -1); // 從倒數第3到倒數第2(索引2和3)
console.log(sub3); // [3, 4]
特性:返回新數組(原數組不變),參數支持負數(表示從末尾開始),slice(0)
可用于復制數組。
3.1.2 concat()
:合并數組
const arr1 = [1, 2];
const arr2 = [3, 4];
const arr3 = [5];// 合并多個數組
const merged = arr1.concat(arr2, arr3, 6); // 可添加非數組元素
console.log(merged); // [1, 2, 3, 4, 5, 6]
console.log(arr1); // [1, 2](原數組不變)
特性:合并多個數組或值,返回新數組(原數組不變),類似ES6的擴展運算符[...arr1, ...arr2]
。
3.2 元素查找
3.2.1 indexOf()
與lastIndexOf()
:查找索引
const fruits = ['apple', 'banana', 'apple', 'orange'];// indexOf(元素, 起始索引):從前往后找,返回首次出現的索引
console.log(fruits.indexOf('apple')); // 0
console.log(fruits.indexOf('apple', 1)); // 2(從索引1開始找)
console.log(fruits.indexOf('grape')); // -1(未找到)// lastIndexOf(元素):從后往前找,返回最后出現的索引
console.log(fruits.lastIndexOf('apple')); // 2
特性:indexOf
從頭部開始,lastIndexOf
從尾部開始;返回元素索引,未找到返回-1
;使用嚴格相等(===
)比較,無法查找引用類型元素。
3.2.2 includes()
:判斷元素是否存在
const nums = [1, 2, 3, NaN];// 基礎用法
console.log(nums.includes(2)); // true
console.log(nums.includes(5)); // false// 特殊:能識別NaN(indexOf不能)
console.log(nums.includes(NaN)); // true
console.log(nums.indexOf(NaN)); // -1(indexOf的缺陷)
特性:返回布爾值(是否包含元素),支持NaN
判斷(比indexOf
更友好),第二個參數可指定起始索引。
3.2.3 find()
與findIndex()
:查找符合條件的元素
const users = [{ id: 1, name: 'Alice' },{ id: 2, name: 'Bob' },{ id: 3, name: 'Alice' }
];// find(回調函數):返回第一個符合條件的元素
const alice = users.find(user => user.name === 'Alice');
console.log(alice); // { id: 1, name: 'Alice' }// findIndex(回調函數):返回第一個符合條件的元素索引
const aliceIndex = users.findIndex(user => user.name === 'Alice');
console.log(aliceIndex); // 0// 未找到時
console.log(users.find(user => user.id === 10)); // undefined
console.log(users.findIndex(user => user.id === 10)); // -1
特性:接受回調函數(item, index, arr) => 條件
,返回第一個符合條件的元素(find
)或索引(findIndex
);適合查找對象數組,支持復雜條件。
3.3 數組轉換
3.3.1 join()
:數組轉字符串
const arr = ['a', 'b', 'c'];// 1. 默認用逗號分隔
console.log(arr.join()); // "a,b,c"// 2. 指定分隔符
console.log(arr.join('-')); // "a-b-c"// 3. 空字符串分隔(拼接成連續字符串)
console.log(arr.join('')); // "abc"
特性:將數組元素拼接為字符串,返回字符串;與String.split()
互為逆操作。
3.3.2 toString()
:數組轉字符串(簡化版)
const arr = [1, 2, 3];
console.log(arr.toString()); // "1,2,3"(等價于join())
特性:默認用逗號分隔,功能簡單,不如join()
靈活。
四、遍歷與迭代方法
這類方法用于遍歷數組并執行操作,是處理數組數據的核心工具。
4.1 基礎遍歷
4.1.1 forEach()
:遍歷元素
const nums = [1, 2, 3];
let sum = 0;// forEach(回調函數):無返回值
nums.forEach((num, index, arr) => {sum += num;console.log(`索引${index}的值:${num}`);
});
console.log('總和:', sum); // 6
特性:無返回值(undefined
),無法通過break
中斷遍歷(需用try/catch
或return
跳過當前元素);適合簡單的遍歷操作。
4.2 映射與過濾
4.2.1 map()
:映射新數組
const numbers = [1, 2, 3, 4];// map(回調函數):返回新數組(每個元素為回調返回值)
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8]
console.log(numbers); // [1, 2, 3, 4](原數組不變)// 對象數組映射
const users = [{ name: 'A' }, { name: 'B' }];
const names = users.map(user => user.name);
console.log(names); // ['A', 'B']
特性:返回新數組(長度與原數組相同),原數組不變;適合數據轉換(如從對象數組中提取特定屬性)。
4.2.2 filter()
:過濾元素
const numbers = [1, 2, 3, 4, 5, 6];// filter(回調函數):返回符合條件的元素組成的新數組
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4, 6]// 對象數組過濾
const users = [{ name: 'A', age: 17 },{ name: 'B', age: 20 },{ name: 'C', age: 25 }
];
const adults = users.filter(user => user.age >= 18);
console.log(adults); // [B(20), C(25)]
特性:返回新數組(包含所有符合條件的元素),原數組不變;適合數據篩選。
4.3 聚合與檢測
4.3.1 reduce()
:累加計算
const numbers = [1, 2, 3, 4];// reduce(回調函數, 初始值):返回累加結果
const sum = numbers.reduce((acc, num) => {return acc + num; // acc為累加器,num為當前元素
}, 0); // 初始值為0
console.log('總和:', sum); // 10// 計算數組中每個元素出現的次數
const fruits = ['apple', 'banana', 'apple'];
const count = fruits.reduce((acc, fruit) => {acc[fruit] = (acc[fruit] || 0) + 1;return acc;
}, {}); // 初始值為對象
console.log(count); // { apple: 2, banana: 1 }
特性:從左到右遍歷,通過累加器acc
聚合結果,功能強大(可實現sum
、max
、groupBy
等);reduceRight()
從右到左遍歷,用法類似。
4.3.2 every()
與some()
:條件檢測
const scores = [80, 90, 75, 60];// every():所有元素都符合條件才返回true
const allPass = scores.every(score => score >= 60);
console.log(allPass); // true(所有分數≥60)// some():至少一個元素符合條件就返回true
const hasExcellent = scores.some(score => score >= 90);
console.log(hasExcellent); // true(有一個分數≥90)
特性:every
類似“邏輯與”(全滿足),some
類似“邏輯或”(有一個滿足);返回布爾值,且會短路(every
遇到不滿足的元素立即返回false
,some
遇到滿足的立即返回true
)。
五、方法對比與最佳實踐
5.1 方法選擇指南
需求場景 | 推薦方法 | 替代方法 |
---|---|---|
尾部添加元素 | push() | splice(arr.length, 0, x) |
尾部刪除元素 | pop() | splice(arr.length-1, 1) |
截取子數組 | slice() | - |
數組映射(轉換) | map() | - |
數組過濾 | filter() | - |
累加計算 | reduce() | forEach() (較繁瑣) |
查找對象數組元素 | find() /findIndex() | forEach() (需手動判斷) |
判斷元素是否存在 | includes() | indexOf() !== -1 |
5.2 性能與注意事項
- 修改原數組 vs 返回新數組:
- 需保留原數組時,優先用
slice()
、map()
等不修改原數組的方法; - 頻繁操作大型數組時,
push()
比concat()
性能更好(因concat()
返回新數組)。
- 需保留原數組時,優先用
- 遍歷中斷:
forEach()
無法用break
中斷,若需中斷可改用for
循環或some()
(通過return true
中斷)。
- 引用類型處理:
- 數組方法對對象元素的操作會影響原對象(因對象是引用傳遞):
const users = [{ name: 'A' }]; users.map(user => { user.age = 18; }); // 修改原對象 console.log(users); // [{ name: 'A', age: 18 }]
六、常見問題與避坑指南
6.1 sort()
的默認排序陷阱
const nums = [10, 2, 23];
nums.sort(); // 錯誤:按字符串排序,結果[10, 2, 23]
nums.sort((a, b) => a - b); // 正確:數值升序,結果[2, 10, 23]
解決方案:始終傳入比較函數處理數值排序。
6.2 indexOf()
無法識別NaN
const arr = [NaN];
console.log(arr.indexOf(NaN)); // -1(錯誤)
console.log(arr.includes(NaN)); // true(正確)
解決方案:判斷NaN
時用includes()
,不用indexOf()
。
6.3 map()
返回新數組的長度不變
const arr = [1, 2, 3];
const newArr = arr.map(num => {if (num > 1) return num * 2;// 未返回值時,默認返回undefined
});
console.log(newArr); // [undefined, 4, 6](長度仍為3)
解決方案:map()
適合“一對一”轉換,若需過濾元素應配合filter()
:
const newArr = arr.filter(num => num > 1).map(num => num * 2); // [4, 6]
總結:數組方法的核心要點
- 區分修改與不修改原數組的方法,根據是否需要保留原數據選擇;
- 遍歷與轉換方法(
map()
、filter()
、reduce()
)是處理復雜數據的核心,需熟練掌握;- 查找方法中,
find()
適合對象數組,includes()
適合簡單元素判斷;- 注意方法的性能差異和特殊場景(如
sort()
的比較函數、includes()
對NaN
的支持)。
若這篇內容幫到你,動動手指支持下!關注不迷路,干貨持續輸出!
ヾ(′? ˋ)ノヾ(′? ˋ)ノヾ(′? ˋ)ノヾ(′? ˋ)ノヾ(′? ˋ)ノ