在JavaScript中反转一个数组而不改变原始数组

363

Array.prototype.reverse 可以在原地(通过变异)反转数组的内容...

是否有一种类似简单的策略可以在不改变原始数组内容的情况下反转数组(无变异)?


这里有一个比较不同选项的基准测试:http://jsben.ch/HZ7Zp - Solomon Ucko
16个回答

678
你可以使用 slice() 来复制,然后 reverse() 它。
var newarray = array.slice().reverse();

var array = ['a', 'b', 'c', 'd', 'e'];
var newarray = array.slice().reverse();

console.log('a', array);
console.log('na', newarray);


10
@Alex slice - 如果省略 begin 参数,则 slice 方法从索引 0 开始。因此,它与 array.slice(0) 相同。 - Arun P Johny
4
我认为@Alex的意思是“在你的回答中解释它”……无论如何,这是一个很棒的解决方案。谢谢! - sfletche
66
我不建议使用这种方法,因为这样做会产生很大的误导。当你使用slice()时并没有实际切割任何内容,这会让人非常困惑。如果你需要一个不可变的反转数组,只需创建一个全新的拷贝:const newArray = [...array].reverse() - Oleg Matei
11
我们能不能花点时间认识一下 JavaScript 还需要进步多少?这就是我们最好的选择了吗?加油啊,JavaScript!再成熟一些吧。 - Gant Laborde
9
整个数组的切片仍然是切片。如果你理解Array#slice的作用,这并不令人困惑。扩展语法没有任何更容易理解的理由。 - Unmitigated
显示剩余2条评论

220

在ES6中:

const newArray = [...array].reverse()

2
这个的表现如何与被接受的答案相比?它们是否相同? - Katie
16
我一直发现使用.slice更快。 - James Coyle
10
这句话的意思是:“@JamesCoyle,这可能与浏览器和操作系统有关,但是 slice 可能更快,因为 [...] 是一种通用的可迭代转数组方法,所以不能做出太多的假设。此外,由于 slice 已经存在很长时间了,它可能更好地优化过。” - Brian M. Hunt
我的想法完全一样。 - James Coyle
2
请参考 https://dev59.com/1W865IYBdhLWcg3wHKzu#52929821 了解不同版本复制数组的基准测试。 - Peter T.

19

const originalArray = ['a', 'b', 'c', 'd', 'e', 'f'];

const newArray = Array.from(originalArray).reverse();

console.log(newArray);


运行良好!谢谢。 - Ali Abbas
我喜欢这个是因为意图更明确。slice()创建一个重复的数组并不明显。 - icc97
你可以这样读它:“从originalArray创建一个新的数组,然后将其反转”。 - icc97

16

另一种ES6变体:

我们还可以使用.reduceRight()创建一个反转数组,而不实际反转它。

let A = ['a', 'b', 'c', 'd', 'e', 'f'];

let B = A.reduceRight((a, c) => (a.push(c), a), []);

console.log(B);

有用的资源:


7
reduceRight非常慢。 - Dragos Rizescu
1
这里有一个你可以玩的 repo:(不是最好的例子,但我曾经在 React 上与某人争论过这个参数,所以我把它放在一起):https://github.com/rizedr/reduce-vs-reduceRight - Dragos Rizescu
4
(a,c)=> a.concat([c]) 这种写法比 (a,c) => (a.push(c),a) 更符合惯用语言的表达方式。 - Kyle Lin
2
@DragosRizescu 看起来这是三个最佳答案中最快的一个。https://jsperf.com/reverse-vs-slice-reverse/3 - DBosley
@非缓和的 是的,大多数情况下是这样。但如果c是一个数组,那么结果行为将与OP预期的不同。 - Kyle Lin
显示剩余3条评论

7

尝试这个递归解决方案:

const reverse = ([head, ...tail]) => 
    tail.length === 0
        ? [head]                       // Base case -- cannot reverse a single element.
        : [...reverse(tail), head]     // Recursive case

reverse([1]);               // [1]
reverse([1,2,3]);           // [3,2,1]
reverse('hello').join('');  // 'olleh' -- Strings too!                              

2
这看起来会在空数组时出错。 - Matthias

7
有多种方法可以在不修改数组的情况下进行反转。其中两种方法是:
var array = [1,2,3,4,5,6,7,8,9,10];

// Using slice
var reverseArray1 = array.slice().reverse(); // Fastest

// Using spread operator
var reverseArray2 = [...array].reverse();

// Using for loop 
var reverseArray3 = []; 
for(var i = array.length-1; i>=0; i--) {
  reverseArray.push(array[i]);
}

性能测试 http://jsben.ch/guftu


你写的 array.splice()slice() 方法之间有性能差异吗? - S.Serpooshan
2
应该使用 slice() 而不是 splice()。后者会返回一个空数组。 - rooch84

6

使用.reduce()和展开操作符的ES6替代方法。

const foo = [1, 2, 3, 4];
const bar = foo.reduce((acc, b) => ([b, ...acc]), []);

基本上它的作用是创建一个新数组,其中包含foo中的下一个元素,然后在b之后的每次迭代中展开累积的数组。
[]
[1] => [1]
[2, ...[1]] => [2, 1]
[3, ...[2, 1]] => [3, 2, 1]
[4, ...[3, 2, 1]] => [4, 3, 2, 1]

或者使用前面提到的.reduceRight(),但不带.push()变异。

const baz = foo.reduceRight((acc, b) => ([...acc, b]), []);

5

ES2023 数组方法 toReversed()

toReversed() 方法将调用数组对象的元素以相反的顺序进行转置,并返回一个新的数组。

const items = [1, 2, 3];
console.log(items); // [1, 2, 3]

const reversedItems = items.toReversed();
console.log(reversedItems); // [3, 2, 1]
console.log(items); // [1, 2, 3]

浏览器兼容性

enter image description here


3

有一个新的tc39提案,它向Array添加了一个toReversed方法,该方法返回数组的副本而不修改原始数组。

提案中的示例:

const sequence = [1, 2, 3];
sequence.toReversed(); // => [3, 2, 1]
sequence; // => [1, 2, 3]

当前处于第三阶段,预计浏览器引擎很快就会实现该功能,但在此之前,可以通过此处或者core-js中提供的polyfill实现。


2
const arrayCopy = Object.assign([], array).reverse()

这个解决方案:
- 成功复制了数组 - 不会改变原始数组 - 看起来像是在做它应该做的事情

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接