JavaScript之数组方法详解

JavaScript之数组方法详解

    • 一、数组的创建与基础特性
      • [1.1 数组的创建方式](#1.1 数组的创建方式)
      • [1.2 数组的核心特性](#1.2 数组的核心特性)
    • 二、修改原数组的方法
      • [2.1 添加/删除元素](#2.1 添加/删除元素)
        • [2.1.1 `push()`:尾部添加元素](#2.1.1 push():尾部添加元素)
        • [2.1.2 `pop()`:尾部删除元素](#2.1.2 pop():尾部删除元素)
        • [2.1.3 `unshift()`:头部添加元素](#2.1.3 unshift():头部添加元素)
        • [2.1.4 `shift()`:头部删除元素](#2.1.4 shift():头部删除元素)
        • [2.1.5 `splice()`:指定位置添加/删除](#2.1.5 splice():指定位置添加/删除)
      • [2.2 排序与反转](#2.2 排序与反转)
        • [2.2.1 `reverse()`:反转数组](#2.2.1 reverse():反转数组)
        • [2.2.2 `sort()`:排序数组](#2.2.2 sort():排序数组)
    • 三、不修改原数组的方法
      • [3.1 数组截取与合并](#3.1 数组截取与合并)
        • [3.1.1 `slice()`:截取子数组](#3.1.1 slice():截取子数组)
        • [3.1.2 `concat()`:合并数组](#3.1.2 concat():合并数组)
      • [3.2 元素查找](#3.2 元素查找)
        • [3.2.1 `indexOf()`与`lastIndexOf()`:查找索引](#3.2.1 indexOf()lastIndexOf():查找索引)
        • [3.2.2 `includes()`:判断元素是否存在](#3.2.2 includes():判断元素是否存在)
        • [3.2.3 `find()`与`findIndex()`:查找符合条件的元素](#3.2.3 find()findIndex():查找符合条件的元素)
      • [3.3 数组转换](#3.3 数组转换)
        • [3.3.1 `join()`:数组转字符串](#3.3.1 join():数组转字符串)
        • [3.3.2 `toString()`:数组转字符串(简化版)](#3.3.2 toString():数组转字符串(简化版))
    • 四、遍历与迭代方法
      • [4.1 基础遍历](#4.1 基础遍历)
        • [4.1.1 `forEach()`:遍历元素](#4.1.1 forEach():遍历元素)
      • [4.2 映射与过滤](#4.2 映射与过滤)
        • [4.2.1 `map()`:映射新数组](#4.2.1 map():映射新数组)
        • [4.2.2 `filter()`:过滤元素](#4.2.2 filter():过滤元素)
      • [4.3 聚合与检测](#4.3 聚合与检测)
        • [4.3.1 `reduce()`:累加计算](#4.3.1 reduce():累加计算)
        • [4.3.2 `every()`与`some()`:条件检测](#4.3.2 every()some():条件检测)
    • 五、方法对比与最佳实践
      • [5.1 方法选择指南](#5.1 方法选择指南)
      • [5.2 性能与注意事项](#5.2 性能与注意事项)
    • 六、常见问题与避坑指南
      • [6.1 `sort()`的默认排序陷阱](#6.1 sort()的默认排序陷阱)
      • [6.2 `indexOf()`无法识别`NaN`](#6.2 indexOf()无法识别NaN)
      • [6.3 `map()`返回新数组的长度不变](#6.3 map()返回新数组的长度不变)

数组是JavaScript中最常用的数据结构之一,掌握数组方法是高效处理数据的基础,本文我将系统梳理JavaScript数组的常用方法,从基础的添加删除到复杂的遍历转换,并结合实例解析每种方法的用法、特性及适用场景,帮你彻底搞懂数组操作的核心技巧。

一、数组的创建与基础特性

在学习方法之前,先回顾数组的创建方式,这是后续操作的基础:

1.1 数组的创建方式

javascript 复制代码
// 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():尾部添加元素
javascript 复制代码
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():尾部删除元素
javascript 复制代码
const lastFruit = fruits.pop(); // 删除最后一个元素
console.log(fruits); // ['apple', 'banana', 'orange']
console.log(lastFruit); // 'grape'(返回被删除的元素)

特性:无参数,删除最后一个元素,返回被删除元素。

2.1.3 unshift():头部添加元素
javascript 复制代码
const newLength = fruits.unshift('mango'); // 头部添加
console.log(fruits); // ['mango', 'apple', 'banana', 'orange']
console.log(newLength); // 4(返回新长度)

特性 :接受任意数量参数,添加到数组头部,返回新长度(性能比push()差,因需移动所有元素)。

2.1.4 shift():头部删除元素
javascript 复制代码
const firstFruit = fruits.shift(); // 删除第一个元素
console.log(fruits); // ['apple', 'banana', 'orange']
console.log(firstFruit); // 'mango'(返回被删除元素)

特性 :无参数,删除第一个元素,返回被删除元素(性能较差,同unshift())。

2.1.5 splice():指定位置添加/删除
javascript 复制代码
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():反转数组
javascript 复制代码
const arr = [1, 2, 3];
arr.reverse();
console.log(arr); // [3, 2, 1]

特性:直接反转原数组,返回反转后的数组(与原数组引用相同)。

2.2.2 sort():排序数组
javascript 复制代码
// 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():截取子数组
javascript 复制代码
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():合并数组
javascript 复制代码
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():查找索引
javascript 复制代码
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():判断元素是否存在
javascript 复制代码
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():查找符合条件的元素
javascript 复制代码
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():数组转字符串
javascript 复制代码
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():数组转字符串(简化版)
javascript 复制代码
const arr = [1, 2, 3];
console.log(arr.toString()); // "1,2,3"(等价于join())

特性 :默认用逗号分隔,功能简单,不如join()灵活。

四、遍历与迭代方法

这类方法用于遍历数组并执行操作,是处理数组数据的核心工具。

4.1 基础遍历

4.1.1 forEach():遍历元素
javascript 复制代码
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/catchreturn跳过当前元素);适合简单的遍历操作。

4.2 映射与过滤

4.2.1 map():映射新数组
javascript 复制代码
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():过滤元素
javascript 复制代码
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():累加计算
javascript 复制代码
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聚合结果,功能强大(可实现summaxgroupBy等);reduceRight()从右到左遍历,用法类似。

4.3.2 every()some():条件检测
javascript 复制代码
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遇到不满足的元素立即返回falsesome遇到满足的立即返回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中断)。
  • 引用类型处理

    • 数组方法对对象元素的操作会影响原对象(因对象是引用传递):
    javascript 复制代码
    const users = [{ name: 'A' }];
    users.map(user => { user.age = 18; }); // 修改原对象
    console.log(users); // [{ name: 'A', age: 18 }]

六、常见问题与避坑指南

6.1 sort()的默认排序陷阱

javascript 复制代码
const nums = [10, 2, 23];
nums.sort(); // 错误:按字符串排序,结果[10, 2, 23]
nums.sort((a, b) => a - b); // 正确:数值升序,结果[2, 10, 23]

解决方案:始终传入比较函数处理数值排序。

6.2 indexOf()无法识别NaN

javascript 复制代码
const arr = [NaN];
console.log(arr.indexOf(NaN)); // -1(错误)
console.log(arr.includes(NaN)); // true(正确)

解决方案 :判断NaN时用includes(),不用indexOf()

6.3 map()返回新数组的长度不变

javascript 复制代码
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()

javascript 复制代码
const newArr = arr.filter(num => num > 1).map(num => num * 2); // [4, 6]

总结:数组方法的核心要点

  1. 区分修改与不修改原数组的方法,根据是否需要保留原数据选择;
  2. 遍历与转换方法map()filter()reduce())是处理复杂数据的核心,需熟练掌握;
  3. 查找方法 中,find()适合对象数组,includes()适合简单元素判断;
  4. 注意方法的性能差异和特殊场景(如sort()的比较函数、includes()NaN的支持)。
    若这篇内容帮到你,动动手指支持下!关注不迷路,干货持续输出!

ヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノ

相关推荐
hackchen4 分钟前
Go与JS无缝协作:Goja引擎实战之错误处理最佳实践
开发语言·javascript·golang
你的人类朋友1 小时前
🤔什么时候用BFF架构?
前端·javascript·后端
知识分享小能手1 小时前
Bootstrap 5学习教程,从入门到精通,Bootstrap 5 表单验证语法知识点及案例代码(34)
前端·javascript·学习·typescript·bootstrap·html·css3
我命由我123452 小时前
前端开发问题:SyntaxError: “undefined“ is not valid JSON
开发语言·前端·javascript·vue.js·json·ecmascript·js
Jokerator3 小时前
深入解析JavaScript获取元素宽度的多种方式
javascript·css
海天胜景3 小时前
vue3 当前页面方法暴露
前端·javascript·vue.js
GISer_Jing3 小时前
前端面试常考题目详解
前端·javascript
中微子4 小时前
JavaScript 防抖与节流:从原理到实践的完整指南
前端·javascript
天天向上10244 小时前
Vue 配置打包后可编辑的变量
前端·javascript·vue.js
芬兰y4 小时前
VUE 带有搜索功能的穿梭框(简单demo)
前端·javascript·vue.js