JavaScript多维数组的创建与访问实例演示
本文详细讲解JavaScript多维数组的核心原理、多种创建方式、安全访问与修改技巧,深入剖析fill方法引用陷阱,并提供完整的实例演示与排坑指南。
一、JavaScript 多维数组的底层机制
在传统编译型语言中,二维数组通常被分配在连续的内存块中,通过行主序或列主序换算偏移量。然而在 JavaScript 中,数组属于引用类型的对象,元素类型不受限制。
当我们在 JavaScript 中创建一个多维数组时,外层数组实际上保存的只是指向各个内层子数组的“内存引用”。这也赋予了 JavaScript 极大的灵活性:每个子数组的长度可以不相同,从而轻松构建所谓的“不规则多维数组”(Jagged Array)。
二、多维数组的三种主流创建方式
根据数据是已知的固定值还是需要动态初始化,通常有以下三种创建手段:
1. 字面量直接初始化(适合静态数据)
如果初始数据已知,最简明的方式是嵌套使用方括号 []:
// 创建一个 3x3 的静态数字矩阵
const matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
console.log(matrix.length); // 输出 3 (行数)
console.log(matrix[0].length); // 输出 3 (第0行的列数)
2. 嵌套循环动态创建(适合维度已知且需计算初值)
当行数与列数是动态变量时,可以通过双层 for 循环按部就班地初始化:
const rows = 3;
const cols = 4;
const grid = [];
for (let i = 0; i < rows; i++) {
grid[i] = []; // 显式初始化第 i 行的数组
for (let j = 0; j < cols; j++) {
grid[i][j] = i * cols + j; // 动态计算赋予初值
}
}
console.log(grid);
// 结果:
// [ [0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11] ]
3. Array.from 声明式创建(现代、安全且简洁)
ES6 提供的 Array.from() 可以在接收长度对象的同时,传入映射回调函数:
const rows = 3;
const cols = 3;
// 创建 3x3、初始全为 0 的矩阵
const zeroMatrix = Array.from({ length: rows }, () => Array(cols).fill(0));
console.log(zeroMatrix);
// [ [0, 0, 0], [0, 0, 0], [0, 0, 0] ]
new Array(rows).fill([])
很多开发者为了简便,会写出 const badMatrix = new Array(3).fill([])。这是严重的逻辑漏洞!因为 fill([]) 只创建了一个内层数组实例,并将同一个数组对象的内存引用复制给了每一行。修改 badMatrix[0][0] = 'X' 会导致所有行都被同时修改!
三、多维数组的访问与数据修改
访问多维数组元素使用连续的方括号语法:array[index1][index2]...[indexN]。
1. 坐标索引与寻址逻辑
对于二维数组 matrix[row][col],执行顺序分为两步:
- 首先执行
matrix[row],获取索引为row的整行子数组; - 紧接着在返回的子数组上执行
[col],获取对应的具体列项。
2. 常见报错与防御性安全访问
在访问不存在的单元格时,最常见的致命报错是:
const table = [[1, 2]];
// 试图读取不存在的第 5 行第 0 列
console.log(table[5][0]);
// 抛出异常: Uncaught TypeError: Cannot read properties of undefined (reading '0')
原因剖析:table[5] 返回的是 undefined,而在 undefined 上再使用 [0] 就会导致脚本崩溃中断。
安全访问方案:使用现代 JavaScript 的可选链操作符 ?.:
// 使用可选链操作符安全读取
const val = table[5]?.[0];
console.log(val); // 得到 undefined,程序平稳运行而不崩溃
// 配合空值合并操作符 ?? 提供默认保底值
const safeValue = table[5]?.[0] ?? "空值";
console.log(safeValue); // 输出 "空值"
3. 元素的更新与修改
修改多维数组指定位置的元素只需结合赋值运算符:
const board = [
['-', '-', '-'],
['-', '-', '-'],
['-', '-', '-']
];
// 将中心位置修改为 'O'
board[1][1] = 'O';
// 将左上角修改为 'X'
board[0][0] = 'X';
console.log(board);
// [ ['X', '-', '-'], ['-', 'O', '-'], ['-', '-', '-'] ]
四、多维数组的遍历与处理技巧
根据处理诉求的不同,遍历多维数组主要有以下四种常用模式:
模式 1:传统双层 for 循环(兼顾索引坐标与最佳性能)
const matrix = [
[10, 20],
[30, 40]
];
for (let r = 0; r < matrix.length; r++) {
for (let c = 0; c < matrix[r].length; c++) {
console.log(`坐标 [${r}, ${c}] 的数值是: ${matrix[r][c]}`);
}
}
模式 2:for...of 循环(语义清晰、无需索引)
for (const row of matrix) {
for (const item of row) {
console.log("元素值:", item);
}
}
模式 3:函数式高阶方法 map / forEach(适合转换数据)
// 将矩阵中所有数值放大 2 倍
const doubled = matrix.map(row => row.map(val => val * 2));
// [ [20, 40], [60, 80] ]
模式 4:使用 flat() 进行一维展平
const cube = [
[[1, 2], [3, 4]],
[[5, 6], [7, 8]]
];
// flat(depth) 展平指定层级,flat(Infinity) 完全展平
const flatArray = cube.flat(2);
console.log(flatArray); // [1, 2, 3, 4, 5, 6, 7, 8]
五、综合实战案例:井字棋(Tic-Tac-Toe)棋盘状态控制
以下是一个可直接在控制台运行的完整实战类:利用二维数组模拟 3x3 棋盘的初始化、落子判定、胜负行检查与格式化打印。
class TicTacToe {
constructor() {
// 1. 安全初始化 3x3 空棋盘
this.board = Array.from({ length: 3 }, () => Array(3).fill(' '));
}
// 2. 落子方法:包含边界与占用校验
makeMove(row, col, player) {
// 防御性校验:检查坐标有效性
if (row < 0 || row >= 3 || col < 0 || col >= 3) {
console.warn(`非法坐标: [${row}, ${col}]`);
return false;
}
if (this.board[row][col] !== ' ') {
console.warn(`位置 [${row}, ${col}] 已被占用!`);
return false;
}
this.board[row][col] = player;
return true;
}
// 3. 检查是否有玩家在某一行获胜
checkRowWin(player) {
for (let r = 0; r < 3; r++) {
if (this.board[r].every(cell => cell === player)) {
return true;
}
}
return false;
}
// 4. 格式化打印当前棋盘
render() {
console.log("-------------");
for (let r = 0; r < 3; r++) {
console.log(`| ${this.board[r].join(' | ')} |`);
console.log("-------------");
}
}
}
// 模拟实战运行流程
const game = new TicTacToe();
game.makeMove(0, 0, 'X');
game.makeMove(1, 1, 'O');
game.makeMove(0, 1, 'X');
game.makeMove(2, 2, 'O');
game.makeMove(0, 2, 'X');
game.render();
console.log("玩家 X 是否行获胜?", game.checkRowWin('X')); // 输出 true
六、全流程最佳实践与总结
为了在实际项目中写出健壮、高效的多维数组操作代码,请牢记以下全流程决策规范:
| 场景需求 | 推荐写法 | 核心优势 / 注意事项 |
|---|---|---|
| 已知固定少量数据 | const m = [[1, 2], [3, 4]]; |
直观清晰,开箱即用。 |
| 已知尺寸动态初始化 | Array.from({ length: r }, () => Array(c).fill(0)); |
每行独立实例化,避免内存引用混淆。 |
| 未知深度深层读取 | matrix[r]?.[c] ?? defaultValue |
避免 TypeError 异常,优雅降级。 |
| 多维结构克隆副本 | structuredClone(matrix) |
真正深拷贝,与原数组彻底切断引用关联。 |
JS动态构建复杂嵌套JSON对象的方法
2026-11-05 15:41:52
JS负数取余为什么是负数?详解余数与模运算区别
2026-11-05 13:34:41
JS 解压出现乱码或报错怎么解决?
2026-11-05 12:32:38
JS 中 startsWith、indexOf 与正则判断字符串前缀的对比
2026-11-04 22:19:43
JS解析Excel后怎么将数据转换为JSON格式
2026-11-04 20:58:28






![JS创建二维数组:为什么Array(n).fill([])会导致数据联动](https://img.javascript.cn/tit/articles/13236/20261102/f618c991891ff37efb12df6af2ce0ccb0da8779a907fe1d2f7275d60c84fdd77.webp)

