JS中的巨坑 - 数组

160 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情

前言

前几天在刷 leetcode 的时候,有一道题需要创建一个二维数组(长宽相等的矩阵),于是写下了如下的代码

function createMatrix(n: number) {
  return Array(n).fill([]);
}

不知道各位小伙伴觉得这样创建矩阵的方式有问题吗 ?



Array.prototype.fill 问题

const matrix = createMatrix(5);

控制台打印的结果:
image.png

这么看似乎真的是创建成功了,但是在使用的时候就出现了大问题
我们想要给矩阵中的 任意一项 赋值

matrix[1][1] = 10;

在控制台的结果:
image.png

结果是 给矩阵中的每个数组索引为 1 的都赋上值了

大家发现问题在哪里的了吗

没发现的的小伙伴不用着急,可以对比这一种创建矩阵的方式

const matrix = [
  [],
  [],
  [],
  [],
  [],
];

这时候,细心的小伙伴就会发现,这里的矩阵中的数组是 不同的数组实例

原来如此,使用 fill 创建的矩阵中的五个数组其实都指向 同一个实例

const matrix = createMatrix(5);
matrix[0] === matrix[1]; // true

当一个对象被传递给 fill 方法的时候,填充数组的是这个对象的引用 -- mdn

const arr = Array(3).fill({}) // [{}, {}, {}];
// 需要注意如果 fill 的参数为引用类型,会导致都执行同一个引用类型
// 如 arr[0] === arr[1] 为 true

fill 第一个参数是引用值 的时候,分发给数组每一项的值都是这个引用值的 指针,所以我们修改 [] 都是修改同一个实例



第二个巨坑 - 数组空位

我们知道了使用直接使用 fill 创建矩阵的问题了

解决办法:遍历数组的每一项,单独给每一项赋值独立的数组实例

于是写下如下代码:

function createMatrix(n: number) {
  return Array(n).map(() => []);
}

这次大家觉得能成功的创建矩阵吗

const matrix = createMatrix(5);

控制台打印的结果:
image.png

好家伙,这次连一项都不创建,为什么会有这个问题呢?

结论:Array.prototype.map 遍历数组会忽略 empty

数组中的 empty 就是我们常说的数组空位,代表没有任何值。

arr[0] === undefined; // true

尽管 emptyundefined 看起来相等 ,但是下面的例子可以很明显看出它们的差异

0 in Array(5); // false
0 in [undefined, undefined, undefined, undefined, undefined]; // true

对于数组空位的差异,ES6之前 会忽略掉空位,而 ES6之后 的方法会当做 undefined 处理,例如

// join 方法忽略空位
[1, , , 5].join('-'); // 1---5

// ... 扩展运算符将空位当做undefined
Array.of(...[, , ,]); // [undefined, undefined, undefined]

由于空位的处理规则非常不统一,所以我们应该避免处理空位。

优雅地创建二维数组(矩阵)

两种思路:

  1. 使用循环 / 遍历,逐项创建数组实例
  2. Array 工厂函数,使用ES6之后的方法避免处理数组空位

  • 将数组空位填充为真实存在的内容
function createMatrix(n: number) {
  return Array(n)
    .fill(0)
    .map(() => []);
}
  • 使用 ES6 之后的方法处理数组空位
function createMatrix(n: number): number[][] {
  return Array.from(Array(n), () => []);
}
function createMatrix(n: number): number[][] {
  return Array.of(...Array(n)).map(() => []);
}