如何在JavaScript中在数组开头添加新的数组元素?

2294

我需要在一个数组的开头添加或者插入元素。

例如,如果我的数组如下所示:

[23, 45, 12, 67]

我的 AJAX 调用响应是 34,我希望更新后的数组如下:

[34, 23, 45, 12, 67]

目前我打算这样做:

var newArray = [];
newArray.push(response);

for (var i = 0; i < theArray.length; i++) {
    newArray.push(theArray[i]);
}

theArray = newArray;
delete newArray;

有没有更好的方法?JavaScript有没有内置的功能可以做到这一点?

我的方法的复杂度是O(n),看到更好的实现会很有趣。


25
如果您需要在数组的开头不断插入元素,使用push语句然后调用reverse比一直调用unshift更快。 - Krisztián Balla
2
@JennyO'Reilly,你应该把这个作为答案发布。完美地匹配了我的使用情况。谢谢。 - rob
2
性能测试: https://jsperf.com/adding-element-to-the-array-start但是每个浏览器的结果都不同。 - Avernikoz
12个回答

3845

使用 unshift 方法,它类似于 push,但是会将元素添加到数组的开头,而不是末尾。

  • unshift/push - 在数组的开头/末尾添加一个元素
  • shift/pop - 移除并返回数组的第一个/最后一个元素

一个简单的图示说明...

   unshift -> [array] <- push
   shift   <- [array] -> pop
 

图表:

          add  remove  start  end
   push    X                   X
    pop           X            X
unshift    X             X
  shift           X      X

查看MDN Array文档。几乎所有能在数组中推入/弹出元素的语言,也都能在数组前/后添加或删除元素(有时称为push_front/pop_front),您不应该自己实现。


正如评论中指出的那样,如果要避免变更原始数组,您可以使用concat,它将两个或多个数组连接在一起。您可以使用它在现有数组的前面或后面函数式地添加单个元素;为此,您需要将新元素转换为单元素数组:

const array = [3, 2, 1]

const newFirstElement = 4

const newArray = [newFirstElement].concat(array) // [ 4, 3, 2, 1 ]

console.log(newArray);

concat 还可以用于添加元素。传递给 concat 的参数可以是任何类型;如果这些参数不是数组,则它们会被隐式转换为只有一个元素的数组:

const array = [3, 2, 1]

const newLastElement = 0

// Both of these lines are equivalent:
const newArray1 = array.concat(newLastElement) // [ 3, 2, 1, 0 ]
const newArray2 = array.concat([newLastElement]) // [ 3, 2, 1, 0 ]

console.log(newArray1);
console.log(newArray2);


67
使用concat更好,因为它返回一个新的数组。非常适合链式调用。例如[thingToInsertToFront].concat(originalArray).reduce(fn).reverse().map(fn)等等... 如果你使用unshift,你无法进行这种链式调用,因为它只会返回数组长度。 - user5900250
10
转移/返回初始位置(shift/unshift)、推入/弹出(push/pop)、剪接(splice)。这些方法的名称非常逻辑清晰。 - linuxunil

1707

array operations image

var a = [23, 45, 12, 67];
a.unshift(34);
console.log(a); // [34, 23, 45, 12, 67]


195
人们需要针对四个日常使用功能的视觉指南的原因是因为这些功能被加密命名了... 为什么unshift不叫做Insert?Shift应该是Remove。等等... - Pascal
99
为什么unshift不叫做Insert?这是来自C编程语言的惯例,其中数组元素被视为堆栈。(详见http://www.perlmonks.org/?node_id=613129 的完整解释) - dreftymac
42
不,"insert"和"remove"不适合用作这个操作的名称;它们暗示了随机访问,而不是在数组前面进行添加/删除。 - user229044
32
我本以为 unshift 应该删除第一个键,而 shift 则是在第一个键插入,但这只是我的一般想法。 - Shannon Hochkins
7
注意,[23, 45, 12, 67].unshift(34) 将不起作用。数组必须先保存在一个变量中,因为 unshift 方法本身会返回一个值。 - vsync

357

使用ES6,使用扩展运算符...

示例

var arr = [23, 45, 12, 67];
arr = [34, ...arr]; // RESULT : [34,23, 45, 12, 67]

console.log(arr)


33
还创建了一个新的数组,对于纯函数很有用。 - devonj
1
这里的性能影响是什么?它比使用unshift()更慢吗? - Peter T.
1
当然,由于它是一个不可变数组(创建一个新数组),所以速度会慢一些。如果你正在处理一个大数组或者性能是你的首要需求,请考虑使用 concat - Abdennour TOUMI
2018年性能已经不再重要,浏览器和Node的新版本都具有相同的性能。 - stackdave
1
@AbdennourTOUMI 只是为了澄清你的评论。它并没有创建一个不可变数组,只是创建了一个新的数组,而不改变现有的数组。 - Flimm
1
@stackdave 当然,性能在2018年(2022年)非常重要。它可能对于你的待办事项应用程序或仅有20个元素的网店界面不重要,但是对于我拥有500 MB类型数组的图形编辑器而言,它非常重要。 - John Weisz

93

另一种方法是通过 concat 实现:

var arr = [1, 2, 3, 4, 5, 6, 7];
console.log([0].concat(arr));

concatunshift 的区别在于,concat 返回一个新数组。它们之间的性能差异可以在这里找到:here

function fn_unshift() {
  arr.unshift(0);
  return arr;
}

function fn_concat_init() {
  return [0].concat(arr)
}

以下是测试结果:

这里输入图片描述


除了添加参考文献之外,将两者的性能比较附加到您的答案中会很好。 - Ivan De Paz Centeno
我刚刚从链接中得到了“_jsPerf暂时不可用,因为我们正在发布v2。请稍后再试_”。这是包含结果而不是链接的另一个好理由。 - Tigger
jsPref 结果:unshift: 25,510 ±3.18%,比最快的慢99%。concat: 2,436,894 ±3.39%,是最快的。 - Illuminator
在最新版本的Safari中,fn_unshift()运行速度更快。 - passatgt
在最新的Safari(v 10)中,fn_unshift()再次变慢了。 - rmcsharry

53

快速备忘单:

对于可能不熟悉C编程语言的人来说,shift/unshift和push/pop这些术语可能有点令人困惑。

如果您不熟悉这些术语,以下是一些替代术语的快速翻译,可以更容易记住:

* array_unshift()  -  (aka Prepend ;; InsertBefore ;; InsertAtBegin )     
* array_shift()    -  (aka UnPrepend ;; RemoveBefore  ;; RemoveFromBegin )

* array_push()     -  (aka Append ;; InsertAfter   ;; InsertAtEnd )     
* array_pop()      -  (aka UnAppend ;; RemoveAfter   ;; RemoveFromEnd ) 

37

使用 ES6 解构(避免更改原始数组):

const newArr = [item, ...oldArr]


7
本质上是Abdennour TOUMI的答案的重复回答。链接 - Jerry

26

你有一个数组:var arr = [23, 45, 12, 67];

如果想在数组开头添加一个元素,可以使用splice方法:

var arr = [23, 45, 12, 67];
arr.splice(0, 0, 34)
console.log(arr);


arr.splice(0, arr.length, 34); - Lior Elrom
1
@LiorElrom 你的代码片段是做什么用的? - Janus Troelsen
@poushy 这是浏览器特定的,在Firefox 54中,unshift速度快了50%(但大多数情况下更易读) - icl7126
@poushy 不再是了。速度慢多了。 - Andrew

26

不会改变原数组

实际上,所有的 unshift/pushshift/pop 都会改变源数组。

unshift/push 会在数组的开头/结尾添加一个元素,而 shift/pop 则会从数组的开头/结尾移除一个元素。

但是还有一些方法可以向数组中添加元素而不进行任何更改,结果将得到一个新的数组。要将元素添加到数组的末尾,请使用以下代码:

const originArray = ['one', 'two', 'three'];
const newItem = 4;

const newArray = originArray.concat(newItem); // ES5
const newArray2 = [...originArray, newItem]; // ES6+

要将内容添加到原始数组的开头,请使用以下代码:

const originArray = ['one', 'two', 'three'];
const newItem = 0;

const newArray = (originArray.slice().reverse().concat(newItem)).reverse(); // ES5
const newArray2 = [newItem, ...originArray]; // ES6+

使用上述方法,您可以在不进行突变的情况下向数组的开头/结尾添加元素。


我只是在originArray的末尾放置了一个slice函数,以防止它的可变性。 - Ahmad Khani
1
太棒了!当涉及到(Redux)状态管理时...这个答案是珍贵的! - Pedro Ferreira
[newItem, ...originArray]; // ES6+ 的语法很棒!完美运行!!谢谢。 - Bat

18

向数组前面添加新元素的快速参考

1. Array#unshift

const list = [23, 45, 12, 67];

list.unshift(34);

console.log(list); // [34, 23, 45, 12, 67];

2. Array#splice

const list = [23, 45, 12, 67];

list.splice(0, 0, 34);

console.log(list); // [34, 23, 45, 12, 67];

3. ES6 扩展运算符...

const list = [23, 45, 12, 67];
const newList = [34, ...list];

console.log(newList); // [34, 23, 45, 12, 67];

4. Array#concat

const list = [23, 45, 12, 67];
const newList = [32].concat(list);

console.log(newList); // [34, 23, 45, 12, 67];

注意:在这些示例中,您可以通过提供更多的插入项来添加多个前置项。


10

如果您需要持续在数组开头插入元素,使用push语句后跟调用reverse比一直调用unshift更快。

基准测试:http://jsben.ch/kLIYf


2
注意:初始数组应为空。 - 192kb
2021年,您的基准测试显示,在所有主要桌面浏览器上(至少在此Mac上),unshift具有全面的胜利。 - Olly Hodgson
@OllyHodgson 在 Ubuntu Linux 上使用 Firefox 进行测试:反向解决方案的速度快了三倍。 - Krisztián Balla

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