返回

JS 数组反转后添加元素异常详解及解决方案

javascript

解析数组反转后添加元素不符合预期的问题

在JavaScript编程过程中,数组操作是很常见的场景。开发者经常会遇到各种与预期不符的结果,尤其是在进行复杂操作时。这里探讨一个具体的现象:一个数组 x 在执行 reverse() 方法并尝试添加另一个数组 y 的元素时,最终结果中只包含一个元素,而不是 y 中的所有预期元素。

问题现象

有两个数组:

let x = [10, 20, 30, 40];
let y = [50, 60];

如果对 x 进行 reverse() 操作后,想要向其中添加 y 中的部分或者全部元素。预期结果类似这样:

// [40, 30, 20, 10, 50, 60];

实际观察到的现象是数组 x 只包含了数组 y 中的第一个元素或者结果与预期有很大的偏差。比如这样:

// (5) [40, 30, 20, 10, 50]

原因分析

导致该问题的原因可能出在对 y 数组元素的引用以及 push 方法的使用上。我们来做进一步分析。

1. y[1,0] 的误解

JavaScript 中,y[1, 0] 的写法并不表示获取 y 数组中索引为 10 的两个元素。 逗号运算符 , 会按顺序评估其操作数,并返回最后一个操作数的值。 y[1, 0] 实际上等同于 y[0],总是返回 y 数组的第一个元素。

2. push 方法与链式调用的影响

JavaScript 的 push() 方法用于向数组末尾添加一个或多个元素,并返回数组的新长度。 当 push()reverse() 链式调用时,push() 会操作被反转后的原始数组 x。同时,reverse() 方法会直接改变原数组 x, 这也是一个容易导致预期外情况的点。

例如:

let x = [1, 2];
let result = x.reverse().push(3); //result 等于 3,而 x 变成了 [2, 1, 3]

注意上面示例的执行顺序以及结果。因为reverse()改变了原始的x。 紧接着的push(3), 操作是在改变后的x基础上进行的。并且返回的是新数组的长度, 而不是数组本身。这很重要!这是关键!

解决方案

针对以上分析,可以有以下几种解决方案:

1. 使用展开语法 (...) 结合 push()

展开语法可以便捷地将一个数组的所有元素展开到另一个数组中。 这样就能解决掉数组 y 添加不进来的问题。

操作步骤:

  1. 反转数组 x
  2. 使用展开语法将数组 y 的所有元素展开,并通过 push() 方法添加到 x 中。

代码示例:

let x = [10, 20, 30, 40];
let y = [50, 60];

x.reverse();
x.push(...y); // 使用展开语法
console.log(x); // 输出:(6) [40, 30, 20, 10, 50, 60]

2. 使用 concat() 方法

concat() 方法用于合并两个或多个数组。该方法不会改变现有数组,而是返回一个新数组。如果需要维持原有数据不变,请用这个方案。

操作步骤:

  1. 创建一个 x 数组的副本,避免直接修改原始数组。
  2. 反转副本数组。
  3. 使用 concat() 方法将副本数组与 y 数组合并。

代码示例:

let x = [10, 20, 30, 40];
let y = [50, 60];

let xCopy = x.slice(); // 创建副本, 可以考虑使用 x.concat()
xCopy.reverse();
let result = xCopy.concat(y);
console.log(result); // 输出:(6) [40, 30, 20, 10, 50, 60]
console.log(x); // 输出:(4) [10, 20, 30, 40]  --原数组保持不变

3. 分步执行, 避免对 push() 返回值的误用。

这种做法更加直观! 每一行都很好懂。不容易出差错。
确保先调用 reverse() 反转数组,然后逐个添加数组 y 中的元素。
需要哪个加哪个,非常灵活。

操作步骤:

  1. 反转数组 x
  2. 循环或者逐个访问 y 数组的元素, 使用push()将需要使用的元素依次添加到 x

代码示例:

let x = [10, 20, 30, 40];
let y = [50, 60];

x.reverse();
x.push(y[0]);
x.push(y[1]);
console.log(x); // 输出:(6) [40, 30, 20, 10, 50, 60]

也可以利用循环:

let x = [10, 20, 30, 40];
let y = [50, 60];

x.reverse();
for (let i = 0; i < y.length; i++) {
    x.push(y[i]);
}

console.log(x); // 输出:(6) [40, 30, 20, 10, 50, 60]

额外安全建议:

代码需要注意边界情况的考虑和异常的处理!防御性编程,会减少问题的出现概率。在进行数组操作之前,确保数组存在且是期望的类型。
如果 x 或者 y 的数据可能来自外部接口。数据格式是无法控制的!一定要进行类型校验,以及必要时做错误处理。 确保代码对非预期的输入有很好的鲁棒性。
对于复杂的数组操作,要添加必要的注释和文档, 以提高代码的可读性和可维护性。方便项目的其他人快速理解, 排查可能出现的问题。
对于重要的方法添加单元测试可以降低回归错误的发生概率。改代码的时候更有底气!可以确保数组的各种操作以及函数的返回值都符合预期。

举个添加边界条件检查的例子:

function safeArrayOperation(x, y) {
  // 检查 x 和 y 是否为数组
  if (!Array.isArray(x) || !Array.isArray(y)) {
    console.error("错误:输入不是数组!");
    return;
  }
  x.reverse();
  x.push(...y);
}

let x = [1,2,3];
let y = [4,5,6];

safeArrayOperation(x,y)

添加了参数类型检查,代码现在健壮性有了一定程度的提升。