在数组基础课程中,我们学习了数组的每个元素都可以存储数据。那么,如果数组的每个元素又是一个数组,会发生什么呢?
让我们从一个简单的思考开始:
如果每个元素不是简单的字符串,而是数组呢?
现在我们有了一个"数组中的数组"!这就是二维数组的由来。
二维数组 = 数组的每个元素又是一个数组
classroom[0][1] 的执行过程:
分步执行:
classroom[0] → 获取第0个元素:["小明", "小红", "小刚"]["小明", "小红", "小刚"][1] → 从该数组中获取第1个元素:"小红"最终结果: classroom[0][1] = "小红"
现实世界的数据经常有结构,适合用二维数组存储:
一维数组的局限性:
二维数组的优势:
记忆口诀:
array[index]array[row][col]现在你已经理解了二维数组是如何从"数组的元素是数组"这个自然想法演变而来的,接下来让我们学习二维数组的类型声明!
和一维数组一样,当我们需要创建空数组时,显式声明类型很重要:
这是你第一次接触 string[][] 这样的二维数组类型语法!让我们详细理解一下:
二维数组有两种等价的写法:
(string[])[] - 括号写法(容易理解)让我们先看这种写法:
理解 (string[])[]:
[](右边):表示这是一个数组(string[])(左边):表示数组中每个元素的类型是字符串数组string[][] - 简洁写法(常用)string[][] 是 (string[])[] 的简化写法:
(string[])[] 的功能完全一样为什么可以这样简化?
[][] 这种写法表示"数组的数组"string[][] 自然就理解为"字符串数组的数组"2 + 3 + 4 和 (2 + 3) + 4 的结果是一样的实际使用建议:
(string[])[] 的含义string[][],因为它更简洁所以 string[][] 的意思是:
现在你已经掌握了二维数组的类型声明,接下来让我们学习二维数组的具体操作方法!
在学习如何访问二维数组元素之前,我们首先需要知道如何获取数组的"形状"——有多少行和多少列。
关键理解:
获取行数:array.length
scores.length = 3 → 表示有3行数据获取列数:array[0].length
array[0]:获取第0行(第一个数组).length:获取这个第0行数组的长度scores[0].length = 3 → 表示第0行有3个元素,即每行有3列为什么用第0行获取列数? 在规则的二维数组中,所有行的列数都是相同的:
scores[0].length = 3scores[1].length = 3scores[2].length = 3语法格式: array[rowIndex][colIndex]
rowIndex:行的索引(从0开始)colIndex:列的索引(从0开始)scores[3][0] 会报错?让我们一步步分析 scores[3][0] 的执行过程:
理解关键点:
scores[3] 返回 undefined(因为没有第4个学生)undefined[0] 等于"给 undefined 取第0个属性",这会立即报错undefined 的属性访问(包括 [索引])都会报错关键规则:对 undefined 的操作限制
✅ 安全的操作:只能进行相等比较
❌ 危险的操作:任何属性访问或方法调用
二维数组访问的三种情况:
✅ 安全:scores[0][3] → 返回 undefined
scores[0] 是数组,访问不存在的索引返回 undefined✅ 安全:scores[3] → 返回 undefined
undefined,没有进一步操作❌ 报错:scores[3][0] → 运行时报错
scores[3] 是 undefined,undefined[0] 违反了对 undefined 的操作规则记忆重点:
undefined.属性 或 undefined[索引] 都会报错安全访问模式:
最佳实践建议:
totalRows = array.length, totalCols = array[0].lengthrowIndex >= 0 && rowIndex < totalCols 和 colIndex >= 0 && colIndex < totalCols修改二维数组元素与访问类似,也需要使用两个索引来精确定位要修改的位置。
语法格式: array[rowIndex][colIndex] = newValue
array[rowIndex][colIndex] 找到目标位置= 将新值赋给该位置理解游戏符号
逐步理解修改过程:
1. 为什么使用 join()?
["⭕", "❌", "⭕"] - 显示方括号和逗号,格式混乱join(" "):⭕ ❌ ⭕ - 用空格分隔,格式清晰美观2. 数组修改的过程:
gameBoard[1][1] → 找到第2行第2列的位置(当前是"⭕")gameBoard[1][1] = "❌" → 将该位置的值改为"❌"遍历二维数组需要使用嵌套循环,但有两种不同的遍历顺序:行优先遍历和列优先遍历。
这是最常用的遍历方式,按照行的顺序访问元素:先完成第0行的所有列,再完成第1行的所有列,以此类推。
嵌套循环的工作原理:
详细执行步骤:
第1轮:外层循环 row=0
row=0col=0:访问 matrix[0][0] = 1col=1:访问 matrix[0][1] = 2col=2:访问 matrix[0][2] = 3第2轮:外层循环 row=1
row=1col=0:访问 matrix[1][0] = 4col=1:访问 matrix[1][1] = 5col=2:访问 matrix[1][2] = 6第3轮:外层循环 row=2
row=2col=0:访问 matrix[2][0] = 7col=1:访问 matrix[2][1] = 8col=2:访问 matrix[2][2] = 9完整的访问顺序:
这种遍历方式按照列的顺序访问元素:先完成第0列的所有行,再完成第1列的所有行,以此类推。
列优先遍历的访问顺序:
| 特点 | 行优先遍历 | 列优先遍历 |
|---|---|---|
| 循环顺序 | 外层行,内层列 | 外层列,内层行 |
| 访问顺序 | 1→2→3→4→5→6→7→8→9 | 1→4→7→2→5→8→3→6→9 |
| 适用场景 | 大多数情况,按行处理 | 按列处理,垂直方向分析 |
| 使用频率 | 常用 | 较少,特殊需求 |
| 直观程度 | 更直观,符合阅读习惯 | 不太直观,需要转换思维 |
什么时候使用哪种遍历?
行优先遍历:99%的情况下使用
列优先遍历:特殊情况下使用
除了手动创建二维数组,我们还可以通过程序动态生成。这在需要创建规律性数据结构时特别有用。
🎲 概率分布逻辑:
我们将使用 Math.random() 来生成随机游戏地图。Math.random() 会生成 0 到 1 之间的随机数:
这样设置可以让地图中大部分是空地,适量的墙,少量的宝箱,游戏体验更平衡。
创建二维数组:
let matrix = [[1, 2], [3, 4]];访问元素:array[row][col]
undefined遍历二维数组:
修改元素:array[row][col] = newValue
你已经完成了数组的深入学习!🎉 从一维数组的基础操作到二维数组的高级应用,你已经具备了处理复杂数据结构的能力。