为什么说数组是 JavaScript 开发者必须精通的数据结构?

当被问及 "数据结构有哪些" 时,若只罗列 "数组、链表、栈、队列、树",难免显得单薄且缺乏逻辑。其实,数据结构可清晰划分为两大核心类别 ------线性结构与非线性结构,各类结构的设计逻辑和应用场景各有侧重,理解其本质能让我们更灵活地运用。​

线性结构是数据按 "一对一" 顺序排列的结构,日常开发中最为常用,核心成员包括数组、链表、栈和队列。其中,数组堪称 "万能基础款",凭借连续的内存空间实现高效随机访问,操作简单、性能稳定,是各类场景的首选;而链表虽不及数组普及,却以离散的内存分配打破了空间连续性限制,能灵活应对动态增删场景,完美弥补了数组的短板。再看栈与队列,二者虽均基于线性逻辑,但遵循截然不同的操作规则:栈是 "先进后出(FILO)" 的 "存取容器",常见于函数调用、表达式求值等场景;队列则是 "先进先出(FIFO)" 的 "排队模型",广泛应用于任务调度、消息队列等需求中。​

非线性结构则是数据呈 "一对多" 或 "多对多" 关联的结构,其中树结构最为核心,尤其以二叉树为考察重点 ------ 无论是算法面试还是实际开发中的搜索、排序场景,二叉树及其衍生结构(如二叉搜索树、红黑树)都占据重要地位。​

接下来,我们将聚焦线性结构中的 "核心成员"------ 数组,详细拆解它的底层原理、使用场景与实战技巧。

新建数组

html 复制代码
<script>
const arr = (new Array(6)).fill(0);
console.log(arr);
const arr2 = new Array(6);
console.log(arr2);
//浏览器显示  [空 ×6]
</script>

用 new Array(length) 创建的数组,在没有初始化之前,它的元素是 "空" 的(empty),而不是 undefined 或其他值。

遍历数组 for(i=0;i<len;i++)

我们来看这段代码:

js 复制代码
const arr = (new Array(6)).fill(0);
const len = arr.length; 
for (let i = 0;i<len;i++){
    console.log(arr[i]);
}

在循环条件中直接使用 arr.length 会有微小的性能开销。

原因如下:

在 JavaScript 中,数组是一个特殊的对象。arr.length 不是一个简单的变量,而是一个访问器属性(Accessor Property) 。这意味着,每次你访问 arr.length 时,JavaScript 引擎实际上是在调用一个隐藏的函数来获取数组的长度。

在早期的 JavaScript 引擎中,这个函数调用的开销是比较明显的。如果在一个循环中(例如执行 10000 次)每次都去调用它,累积的开销就会变得可观。

遍历数组 foreach

我们接着说 forEach,你提到的两点 ------不能 break 和 性能相对较差------ 非常关键,我们来详细展开,并补充一些你可能忽略的细节。


1. forEach 基本用法

javascript 复制代码
const arr = [1, 2, 3];
arr.forEach((item, index, array) => {
  console.log(item, index, array);
});
  • 回调参数:

    • item:当前遍历的元素(必选)
    • index:当前元素的索引(可选)
    • array:正在遍历的原数组(可选)
  • 返回值 :undefined(无论回调里写什么 return,都不会改变这一点)


(1)无法中途退出

forEach 没有像 for 循环那样的 break 或 continue 机制。

  • 想提前终止?做不到:

    javascript 复制代码
    arr.forEach(item => {
      if (item === 2) break; // 报错:Illegal break statement
    });
  • 想跳过当前元素?用 return 模拟 continue:

    javascript 复制代码
    arr.forEach(item => {
      if (item === 2) return; // 跳过当前迭代,进入下一次
      console.log(item); // 输出 1, 3
    });

map

javascript

运行

javascript 复制代码
// map
// 作用:遍历数组,并对数组中的每个元素进行指定操作(加工/转换),
//      然后将所有操作结果收集起来,返回一个全新的数组。
// 核心:不改变原数组,返回一个长度相同、值经过转换的新数组。
// 适用场景:当你需要从一个数组"派生"出另一个结构相似但值不同的数组时。

const arr = [1, 2, 3, 4, 5, 6];
const newArr = arr.map(item => item + 1); // 对每个元素加 1

console.log(arr);    // 输出: [1, 2, 3, 4, 5, 6] (原数组未改变)
console.log(newArr); // 输出: [2, 3, 4, 5, 6, 7] (新数组)

// 回调函数参数详解:
// item: 当前正在处理的数组元素 (必选)
// index: 当前元素在数组中的索引 (可选)
// array: 调用 map 方法的原数组本身 (可选)
const doubledWithIndex = arr.map((item, index) => {
    return `Index ${index}: ${item * 2}`;
});
console.log(doubledWithIndex); // 输出: ["Index 0: 2", "Index 1: 4", "Index 2: 6", ...]

// 与 forEach 的区别:
// 1. forEach 仅用于遍历,没有返回值(返回 undefined)。
// 2. map 会返回一个新数组,更适合数据转换。
// 简单说:想"生成新数组"用 map,想"单纯遍历做操作"用 forEach。

for...of

javascript

运行

javascript 复制代码
// for...of
// 作用:ES6 引入的一种新型循环语句,用于遍历可迭代对象(如 Array, Map, Set, String 等)。
// 核心:提供了一种更简洁、更具可读性的方式来遍历集合中的值。
// 优点:
//   - 语法简洁,可读性远超传统 for 循环。
//   - 避免了 for...in 循环遍历数组时可能遇到的原型链污染问题。
//   - 可以与 break, continue, return 配合使用。

const arr = [1, 2, 3, 4, 5, 6];

// 基本用法
for (let item of arr) {
    console.log(item); // 依次输出: 1, 2, 3, 4, 5, 6
}

// 如果需要索引,可以结合 Array.prototype.entries() 使用
for (let [index, item] of arr.entries()) {
    console.log(`Index ${index}: ${item}`); // 依次输出: "Index 0: 1", "Index 1: 2", ...
}

// 与传统 for 循环对比
// 传统 for 循环:
for (let i = 0; i < arr.length; i++) {
    console.log(arr[i]);
}
// for...of 写法更简洁,意图更明确。

// 与 for...in 对比 (注意:不推荐用 for...in 遍历数组)
// for...in 遍历的是键名(索引),且可能遍历到数组原型上的属性。
for (let key in arr) {
    console.log(key); // 输出: "0", "1", "2", ... (字符串形式的索引)
}

总结对比

特性 / 方法 map() for...of
核心用途 数据转换,生成新数组。 遍历迭代,访问集合中的每个值。
返回值 返回一个新数组。 无返回值 (undefined)。
是否改变原数组 不改变(除非回调函数内手动修改)。 不改变(除非循环体内手动修改)。
适用对象 仅数组 (Array)。 所有可迭代对象 (Array, String, Map, Set, arguments 等)。
中断循环 无法中断(必须遍历完所有元素)。 可以使用 break 或 continue 中断或跳过。
获取索引 回调函数的第二个参数即为索引。 需要配合 arr.entries() 才能方便地获取索引。

何时使用?

  • 使用 map :当你需要将一个数组的每个元素按照某种规则进行转换,最终得到一个新的数组时。例如,从 API 获取用户列表后,提取每个用户的 id 和 name 组成新数组。
  • 使用 for...of :当你需要遍历一个数组或其他可迭代对象,对每个元素执行一些操作(如打印、累加、根据条件进行复杂处理等),并且可能需要中途停止循环时。它是日常遍历任务中替代传统 for 循环和 forEach 的优秀选择。
相关推荐
hnjzsyjyj2 分钟前
洛谷 P1339:[USACO09OCT] Heat Wave G ← Bellman-Ford 算法
数据结构·bellman-ford 算法
Hilaku10 分钟前
我重写了整个项目,但没人感谢我!
前端·javascript·程序员
起个名字费劲死了13 分钟前
VisionMaster集成深度学习算法(基础狗)
人工智能·深度学习·算法
lengxuenong16 分钟前
枚举例题分析
c++·算法·青少年编程
Navigator_Z34 分钟前
LeetCode //C - 1269. Number of Ways to Stay in the Same Place After Some Steps
c语言·算法·leetcode
2601_9657422237 分钟前
短视频脚本创作方法分享:本地生活类账号的起步思路
大数据·人工智能·算法·ai·新媒体运营·生活
55873生态系统手记1 小时前
读完 55873 资产钱包的技术栈,我重新理解了 "合规钱包" 应该长什么样
算法·架构
zach1 小时前
Vue/React SPA 打包部署后,子路由刷新 401 未登录问题彻底解决
前端·nginx·next.js
旋生万物1 小时前
素数螺旋映射 $z_n=n^{1+i}$ 的角分布统计检验与零模型对比
大数据·前端·人工智能·算法·云原生·螺旋生成论·螺旋相位
亿元程序员1 小时前
游戏引擎都没用!纯AI又上线了一款蚂蚁搬家小游戏!
前端