
本文揭示了 JavaScript 中因对象引用共享引发的典型 bug:createBlock() 函数在多次调用后输出不一致,根本原因是 DEFAULT_BLOCK_GRID 被声明为全局 const 数组,虽不可重新赋值,但其内部元素可被反复修改,导致后续调用始终操作同一份内存数据。
本文揭示了 javascript 中因对象引用共享引发的典型 bug:`createblock()` 函数在多次调用后输出不一致,根本原因是 `default_block_grid` 被声明为全局 `const` 数组,虽不可重新赋值,但其内部元素可被反复修改,导致后续调用始终操作同一份内存数据。
问题的核心在于对 const 的常见误解:const DEFAULT_BLOCK_GRID = [[...], [...], [...]] 仅保证变量 DEFAULT_BLOCK_GRID 的绑定不可变(即不能重新指向新数组),但不冻结其内容。该数组本身是引用类型,所有对 newBlock[i][j] = true 的赋值操作,实际上都在直接修改 DEFAULT_BLOCK_GRID 这一共享的底层内存结构。因此,第一次调用 createBlock() 后,DEFAULT_BLOCK_GRID 已被污染;后续调用(无论是否通过 createListOfBlocksGrid)都基于已被修改的“脏”模板,最终统一输出如 [ [true,true,true,true], [true,true,true,true], [true,true,true,false] ] 的错误结果。
✅ 正确解法是确保每次调用 createBlock() 都获得一份全新、独立、未被修改过的默认网格。推荐将 DEFAULT_BLOCK_GRID 移入函数作用域,并使用深拷贝或结构化初始化:
function createBlock(block) {
// ✅ 每次调用都创建全新二维数组(浅拷贝足够,因元素为布尔值)
const DEFAULT_BLOCK_GRID = [
[false, false, false, false],
[false, false, false, false],
[false, false, false, false]
];
let newBlock = DEFAULT_BLOCK_GRID; // 此时 newBlock 是独立副本
for (let i = 0; i < DEFAULT_BLOCK_GRID.length; i++) {
if (block.length <= i) break;
for (let j = 0; j < DEFAULT_BLOCK_GRID[i].length; j++) {
// ⚠️ 原代码有严重笔误:`block.length[i]` 应为 `block[i]?.length`
if (!block[i] || block[i].length <= j) continue;
if (block[i][j] === true) {
newBlock[i][j] = true;
}
}
}
return newBlock;
}? 额外修复说明:原代码中
if (block.length[i] 是语法错误(<code>block.length是数字,不可索引),已修正为if (!block[i] || block[i].length ,避免运行时错误。
同时,createListOfBlocksGrid() 的逻辑也需校准——当前它错误地 push(value) 而非 push(block),应改为:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
function createListOfBlocksGrid() {
const BlocksGrid = [];
for (const value of Object.values(BLOCKS_MATRIX)) {
BlocksGrid.push(createBlock(value)); // ✅ 推入转换后的 block
}
return BlocksGrid;
}? 最佳实践总结:
- 对于用作模板的可变数据结构(如数组、对象),永远避免全局可变模板;
- 使用
const+ 内联初始化(如[[...],[...]])是最轻量且安全的“每次新建”方式; - 若模板复杂,可封装为工厂函数:
const createDefaultGrid = () => [[...], [...]]; - 在调试类似问题时,可用
console.log(JSON.stringify(grid))替代console.log(grid),规避浏览器控制台显示的引用缓存误导。
至此,createBlock() 将严格遵循“相同输入 → 相同输出”的纯函数原则,彻底解决状态污染导致的非确定性行为。

















