JavaScript Array [](保姆级教程)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观
在 JavaScript 开发中,数组(Array)是处理数据的核心工具之一。它像一个灵活的收纳盒,可以存放多种类型的数据,并支持高效的操作。无论是处理用户输入、管理动态数据,还是构建复杂的应用逻辑,数组都是开发者最常依赖的数据结构。本文将从基础概念到高级技巧,系统性地解析 JavaScript 数组的特性与使用方法,帮助初学者和中级开发者掌握这一重要工具。
一、数组的基本概念与特性
1.1 什么是数组?
JavaScript 数组(Array)是一种 有序的、可变的集合数据结构,用于存储多个值。每个值称为数组的“元素”(Element),并按照**索引(Index)**依次排列。索引从 0
开始,因此第一个元素的索引是 0
,第二个是 1
,依此类推。
比喻:可以把数组想象成一个抽屉柜,每个抽屉都有编号(索引),存放不同的物品(元素)。例如:
const myArray = ["苹果", "香蕉", "橘子"];
// 索引 0 → "苹果",索引 1 → "香蕉",索引 2 → "橘子"
1.2 数组的特性
- 动态性:数组长度可变,支持增删元素。
- 异构性:元素可以是任意类型(数字、字符串、对象、函数等)。
- 引用类型:数组是对象,赋值操作会传递引用(即“地址”),而非复制数据本身。
引用类型的比喻:
const arr1 = [1, 2, 3];
const arr2 = arr1; // arr2 是 arr1 的“影子”,指向同一块内存
arr2.push(4);
console.log(arr1); // [1, 2, 3, 4] → 修改 arr2 会影响 arr1
二、创建与访问数组元素
2.1 数组的创建方式
JavaScript 提供了多种创建数组的方式:
方法一:字面量语法(Literal)
这是最常用的方式,语法简洁:
const fruits = ["apple", "banana", "orange"];
方法二:构造函数 Array()
通过 new Array()
初始化数组,但需注意参数传递方式:
// 方式一:传入元素列表
const numbers = new Array(1, 2, 3); // [1, 2, 3]
// 方式二:传入长度(需谨慎使用)
const emptyArray = new Array(3); // [empty ×3],元素为 undefined
2.2 访问与修改元素
通过索引可以直接读取或修改元素:
const colors = ["red", "green", "blue"];
console.log(colors[0]); // "red"
colors[1] = "yellow";
console.log(colors); // ["red", "yellow", "blue"]
注意:
- 如果索引超出范围(如
colors[10]
),会返回undefined
。 - 负数索引(如
colors[-1]
)在 JavaScript 中无效。
三、数组的核心操作方法
JavaScript 提供了丰富的内置方法,覆盖增删、遍历、转换等场景。以下列举常用方法,并通过表格对比其功能与用法:
方法 | 功能描述 | 示例代码 |
---|---|---|
push() | 在末尾添加元素并返回新长度 | arr.push("newItem") → 修改原数组 |
pop() | 移除末尾元素并返回该元素 | const lastItem = arr.pop() |
unshift() | 在开头添加元素并返回新长度 | arr.unshift("firstItem") → 修改原数组 |
shift() | 移除开头元素并返回该元素 | const firstItem = arr.shift() |
slice(start, end) | 截取子数组(不修改原数组) | const subArr = arr.slice(1, 3) |
splice() | 修改数组(增删元素) | arr.splice(2, 0, "new") → 插入元素 |
map() | 遍历并生成新数组 | arr.map(item => item * 2) |
filter() | 筛选符合条件的元素 | arr.filter(num => num > 5) |
3.1 常用方法详解
3.1.1 map()
:遍历与转换
map()
方法对数组的每个元素执行回调函数,并返回一个新数组(不修改原数组)。
const numbers = [1, 2, 3];
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6]
console.log(numbers); // [1, 2, 3] → 原数组未变
3.1.2 filter()
:条件筛选
filter()
根据回调函数的返回值(true
/false
)筛选元素:
const evenNumbers = [1, 2, 3, 4].filter(num => num % 2 === 0);
console.log(evenNumbers); // [2, 4]
3.1.3 reduce()
:聚合计算
reduce()
可将数组元素“累积”为单一值,常用于求和、统计等场景:
const sum = [1, 2, 3, 4].reduce((acc, curr) => acc + curr, 0);
console.log(sum); // 10 → 初始值为 0
四、数组的高级技巧与注意事项
4.1 判断元素是否存在
可以通过 includes()
或 indexOf()
方法快速判断元素是否存在于数组中:
const letters = ["a", "b", "c"];
console.log(letters.includes("b")); // true
console.log(letters.indexOf("d") !== -1); // false
4.2 合并与拆分数组
-
合并:使用扩展运算符(
...
)或concat()
方法:const arr1 = [1, 2]; const arr2 = [3, 4]; const merged = [...arr1, ...arr2]; // [1, 2, 3, 4]
-
拆分:通过
slice()
或splice()
实现,但需注意副作用:const arr = [1, 2, 3, 4]; const firstTwo = arr.slice(0, 2); // [1, 2] → 无副作用 arr.splice(2, 2); // 移除索引 2 开始的 2 个元素 → arr 变为 [1, 2]
4.3 处理空数组与边界情况
- 空数组的判断:直接比较
array.length === 0
,或使用!array.length
。 - 避免索引越界:操作数组前建议先检查长度,例如:
function getLastElement(arr) { return arr.length > 0 ? arr[arr.length - 1] : null; }
五、数组的内存机制与性能优化
5.1 引用类型的内存占用
数组作为对象,存储的是元素的“引用”而非值本身。因此,修改数组元素会影响所有指向该数组的变量:
let arrA = [1, 2];
let arrB = arrA;
arrB[0] = 100;
console.log(arrA); // [100, 2] → arrA 和 arrB 指向同一内存
5.2 性能优化建议
- 避免频繁修改大型数组:
splice()
等操作在长数组中可能耗时较长。 - 使用原生方法代替循环:如
map()
和filter()
内部优化更好。 - 冻结数组:若数组无需修改,可使用
Object.freeze()
防止意外更改:const immutableArr = Object.freeze([1, 2, 3]); immutableArr.push(4); // 抛出错误(非严格模式下可能静默失败)
六、实际案例:构建一个待办事项列表
6.1 需求分析
假设需要实现一个简单的待办事项列表,要求支持以下功能:
- 添加新任务;
- 标记任务为已完成;
- 删除任务;
- 展示任务列表。
6.2 代码实现
class TodoList {
constructor() {
this.todos = [];
}
addTodo(task) {
this.todos.push({ text: task, completed: false });
}
toggleCompleted(index) {
if (index >= 0 && index < this.todos.length) {
this.todos[index].completed = !this.todos[index].completed;
}
}
removeTodo(index) {
this.todos.splice(index, 1);
}
displayTodos() {
console.log("待办事项:");
this.todos.forEach((todo, idx) => {
console.log(`${idx}: [${todo.completed ? "✓" : " "}] ${todo.text}`);
});
}
}
// 使用示例
const list = new TodoList();
list.addTodo("学习 JavaScript 数组");
list.addTodo("完成项目代码");
list.displayTodos();
list.toggleCompleted(0);
list.removeTodo(1);
list.displayTodos();
6.3 输出结果
待办事项:
0: [ ] 学习 JavaScript 数组
1: [ ] 完成项目代码
待办事项:
0: [✓] 学习 JavaScript 数组
结论
JavaScript 数组是一个功能强大且灵活的数据结构,掌握其核心方法与特性,能够显著提升开发效率。从基础的增删操作到高级的内存管理,数组的应用场景贯穿前端开发的各个环节。对于初学者,建议通过实际项目(如待办事项列表)巩固知识;中级开发者则可深入研究数组的原型链方法、迭代器等底层机制,进一步优化代码性能。
记住,实践是掌握数组的最佳途径。通过不断编写代码、调试问题,你将逐渐熟悉 JavaScript Array 的“魔法”,并能用它解决更复杂的开发挑战!