JavaScript数组引用陷阱

问题描述

在使用回溯算法时,我们经常需要保存中间结果。看似简单的数组操作可能藏着一个常见陷阱:

1
2
3
4
5
6
7
8
let result = [];
let path = [];

// 错误示例
result.push(path);

// 正确示例
result.push(path.slice());

为什么会出错?

JavaScript中的数组是引用类型。当我们直接将path加入result时:

1
result.push(path)  // 存储的是引用

这意味着result中的每个元素都指向同一个path数组。当path被修改时,result中所有的记录都会改变。

实际效果

假设有这样的回溯过程:

1
2
3
path = [1,2]  → result = [[1,2]]
path = [1,3] → result = [[1,3], [1,3]] // 注意第一个组合也变了
path = [2,3] → result = [[2,3], [2,3], [2,3]] // 全部变成了相同的值

解决方案

使用slice()创建数组副本:

1
result.push(path.slice())  // 存储独立副本

这样每次存储的都是当前path的独立复制,后续对path的修改不会影响已存储的结果。

其他方法

  • 展开运算符:[...path]
  • Array.from:Array.from(path)
  • concat:[].concat(path)

关键是要理解:什么时候需要副本,什么时候可以用引用。在需要保存状态的场景中,创建副本是更安全的选择。