返回

解剖 ES6 数组 fill() 填充的陷阱:为什么它会导致意外的二维数组?

前端

在 JavaScript 的世界里,数组作为一种强大的数据结构,承载着我们应用程序的关键信息。ES6 为数组提供了便捷的 fill() 方法,允许我们轻松地用特定值填充数组。然而,在使用 fill() 时,一个看似简单的任务却可能导致一个出人意料的陷阱:二维数组的意外生成。

场景再现

让我们从一个看似直观的场景开始。假设我们想将一维数组转换成二维数组。于是,我们愉快地编写了以下代码:

const oneDimArray = [1, 2, 3];
const twoDimArray = new Array(3).fill(oneDimArray);
console.log(twoDimArray);

我们期望输出如下结果:

[[1, 2, 3], [1, 2, 3], [1, 2, 3]]

但结果却是令人震惊的:

[[1, 2, 3], [1, 2, 3], [1, 2, 3]]

咦,为什么每个内数组都指向同一个引用?让我们深入剖析一下问题所在。

填充过程的秘密

fill() 方法有两个主要参数:要填充的值和可选的开始和结束索引。当没有提供开始和结束索引时,fill() 会从头到尾填充整个数组。在我们的案例中,new Array(3) 创建了一个长度为 3 的新数组,然后用 oneDimArray 填充。

乍一看,这个过程似乎没有什么问题。然而,关键在于 fill() 如何处理填充值。它不复制填充值,而是直接使用对填充值的引用。这正是导致意外行为的原因。

当我们用 oneDimArray 填充新数组时,每个元素都是 oneDimArray 本身的引用。这意味着当我们修改新数组中的任何元素时,原始 oneDimArray 也会受到影响。

陷阱的解决方案

要避免这个陷阱,我们需要确保填充值是原始值的副本。有几种方法可以做到这一点:

  • 使用扩展运算符(...): let twoDimArray = new Array(3).fill([...oneDimArray]);
  • 使用 slice() 方法: let twoDimArray = new Array(3).fill(oneDimArray.slice());
  • 使用 map() 方法: let twoDimArray = new Array(3).fill(oneDimArray.map(x => x));

这些方法都会创建 oneDimArray 的副本,从而避免意外的二维数组行为。

结论

ES6 数组的 fill() 方法是一个强大的工具,但它潜藏着填充值引用的陷阱。通过理解这一机制并采用适当的复制策略,我们可以避免意外行为并编写健壮可靠的代码。