当前位置: 首页 > news >正文

JavaScript之数组方法详解

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/catchreturn跳过当前元素);适合简单的遍历操作。

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聚合结果,功能强大(可实现summaxgroupBy等);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遇到不满足的元素立即返回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中断)。
  • 引用类型处理
    • 数组方法对对象元素的操作会影响原对象(因对象是引用传递):
    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]

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

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

若这篇内容帮到你,动动手指支持下!关注不迷路,干货持续输出!
ヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノ

http://www.dtcms.com/a/269752.html

相关文章:

  • VSYNC 深度解析
  • Apollo源码架构解析---附C++代码设计示例
  • 提炼总结—ROS2机器人开发(完结)
  • 【WEB】Polar靶场 16-20题 详细笔记
  • Python实现二分查找算法详解
  • 经典论文 Science子刊:数据驱动的偏微分方程发现 —— Supplementary Materials
  • 找了两个月,没找到工作
  • 【笔记】开源 AI Agent 项目 V1 版本 [新版] 部署 日志
  • 开源 python 应用 开发(四)python文件和系统综合应用
  • go go go 出发咯 - go web开发入门系列(一) helloworld
  • uniapp使用 renderjs 多平台谷歌地图(Google Map)的适配
  • 力扣-31.下一个排列
  • React Native安卓刘海屏适配终极方案:仅需修改 AndroidManifest.xml!
  • 【openGLES】安卓端EGL的使用
  • Javafx教程(1)——初始Javafx
  • 工业HMI的智能化转型:边缘计算与预测性维护的深度融合
  • 自定义RecyclerView的ItemDecoration,用于处理网格布局间距装饰器(支持边缘间距独立控制)
  • ubuntu vscode 点击变量链接进去后 怎么返回原来的位置
  • LocalStorage和SessionStorage的区别和应用
  • 马尔可夫决策过程
  • python办公自动化----使用pandas和os合并多个订单表
  • 【python】 `parse_time_to_seconds` 在功能及健壮性上有以下主要区别
  • ​扣子Coze飞书多维表插件添加数据记录
  • 【UE5】虚幻引擎小百科
  • std::function
  • coze平台AI Agent开发入门之工作流的基本使用方法
  • Redis 缓存机制 及问题场景 及解决方案
  • 接口自动化工具-SoapUI
  • kotlin
  • Ubuntu22.04下微星B850M主板 无wifi模块