在JavaScript中更好的将一个数组拼接到另一个数组的方法

73

有没有一种比这个更好的方法来将一个数组切割并插入另一个数组中的JavaScript代码?

var string = 'theArray.splice('+start+', '+number+',"'+newItemsArray.join('","')+'");';
eval(string);

作为一个经验法则,在JavaScript中应该尽量避免使用eval。使用eval总是(毫无例外)比替代方案更容易出错、更容易产生错误、更难调试,而且速度要慢得多。如果你想使用eval,那么请寻求帮助或从头开始重写代码。eval是JavaScript的祸根。我从未使用过eval。如果我需要执行代码,我会使用更优秀的Function。在这种情况下,如果start是一个字符串,如果number是一个字符串,或者如果newItemsArray中的任何字符串包含引号、换行符或回车符,你的代码可能会失败。 - Jack G
8个回答

142
你可以使用apply来避免eval:

你可以使用apply来避免eval:

var args = [start, number].concat(newItemsArray);
Array.prototype.splice.apply(theArray, args);

apply函数用于调用另一个函数,并提供数组形式的给定上下文和参数,例如:

如果我们调用:

var nums = [1,2,3,4];
Math.min.apply(Math, nums);

apply函数将执行:

Math.min(1,2,3,4);

4
尽管如此,实际上我需要执行Array.prototype.splice.apply(theArray, [start, number].concat(newItemsArray)),因为所有的参数都必须在一个数组中,而不仅仅是一些。 - wheresrhys
3
对于[start, number].concat(array)这种模式表示赞同,这真是个巧妙的方法。 - Claudiu
1
有人尝试在Chrome下输入大量元素吗?大约130K个元素,我们似乎会遇到Stackoverflow异常。请参见:http://jsfiddle.net/DcHCY/ - Gabriel Kohen
2
@Claudiu 另一个巧妙的技巧是将 Array.prototype 替换为 []。这样命令就可以是:[].splice.apply(theArray, [start, removeCount].concat(newItemsArray); - snapfractalpop
非常晚回复 Gabriel Kohen,但使用 apply 或 ... 运算符都存在大数据集的问题,您受到特定 JavaScript 引擎参数限制的限制。 - CSJordan
像 C# 的 params 关键字 一样,splice 允许传入不定数量的参数。但是如果你直接传入一个数组给 splicea.splice(start, number, newItems) -- 它会将 newItems 插入为一个 _单独的元素_。使用 args 数组,apply(而 不是 call)在 es5- 中将我们的 newItems + args 数组“展开”成参数。聪明。 - ruffin

77

更新:ES6版本

如果你在使用ES6编码,可以使用“扩展运算符”(...)。

array.splice(index, 0, ...arrayToInsert);

要了解更多关于展开运算符的内容,请参阅MDN文档


ES5的“旧”方式

如果你将顶部答案包装到一个函数中,你会得到这个:

function insertArrayAt(array, index, arrayToInsert) {
    Array.prototype.splice.apply(array, [index, 0].concat(arrayToInsert));
}

你可以这样使用它:

var arr = ["A", "B", "C"];
insertArrayAt(arr, 1, ["x", "y", "z"]);
alert(JSON.stringify(arr)); // output: A, x, y, z, B, C

您可以在这个jsFiddle中查看:http://jsfiddle.net/luisperezphd/Wc8aS/


有趣的是,我没有意识到函数参数被强制转换为数组,并且展开语法是有效的。 - neaumusic
这个 array.splice(index, 0, ...arrayToInsert); 的解决方案很棒,看起来是最顺畅的。 - Sergey Khmelevskoy

15

这个问题很老了,但是使用ES6,有一种更简单的方法可以使用扩展运算符:

sourceArray.splice(index, 0, ...insertedArray)
如果您在浏览器中使用未编译的javascript,请确保在目标浏览器中支持它,可以在https://kangax.github.io/compat-table/es6/#test-spread_(...)_operator检查。
此外,这可能略微偏离主题,但如果您不想或不需要修改原始数组,而是可以使用新数组,请考虑此方法:
mergedArray = sourceArray.slice(0, index).concat(insertedArray, sourceArray.slice(index))

6

如果您想要一个几乎与splice方法相同的功能,还可以将此函数添加到Array原型中。例如:

Array.prototype.spliceArray = function(index, n, array) {
    return Array.prototype.splice.apply(this, [index, n].concat(array));
}

那么使用方法就很简单了:

var array = ["A","B","C","","E","F"];

array.splice(3,1,"D");
// array is ["A","B","C","D","E","F"]

array.spliceArray(3,3,["1","2","3"]);
// array is ["A","B","C","1","2","3"]

在这里查看它的实际效果:http://jsfiddle.net/TheMadDeveloper/knv2f8bb/1/

一些注意事项:

  • splice 函数直接修改数组,但返回被删除的元素的数组... 而不是被拼接的数组。
  • 虽然通常不建议扩展核心 javascript 类,但对于大多数标准框架来说,这是相对无害的。
  • 扩展 Array 在使用专门的数组类(如 ImageData 数据 Uint8ClampedArray)的情况下将无法正常工作。

1
这里有很多聪明的答案,但你使用splice的原因是它将元素放入当前数组中而不创建另一个数组。如果你必须创建一个数组来与之合并以便使用apply(),那么你就会创建两个额外的垃圾数组!这有点违背了编写晦涩的Javascript的整个目的。此外,如果你不关心内存使用问题(你应该关心),只需使用“dest = src1.concat(src2)”;它更易读。所以这是我保持高效率的最少行数答案。
for( let item of src ) dest.push( item );

或者,如果您想要使用polyfill并获得更好的浏览器支持:

src.forEach( function( x ) { dest.push(x); });

我相信第一个更加高效(performant是一个词),但是它在野外的所有浏览器中都不被支持。


1
上面的答案涉及splice.apply并在一行中插入数组,对于大型数组会导致堆栈溢出。 请参见此示例: http://jsfiddle.net/gkohen/u49ku99q/ 为使其正常工作,您可能需要对插入和原始数组的剩余部分的每个项目进行切片和推送。 请参见fiddle:http://jsfiddle.net/gkohen/g9abppgy/26/
Array.prototype.spliceArray = function(index, insertedArray) {
   var postArray = this.splice(index);
   inPlacePush(this, insertedArray);
   inPlacePush(this, postArray);

   function inPlacePush(targetArray, pushedArray) {
// Not using forEach for browser compatability
       var pushedArrayLength = pushedArray.length;
       for (var index = 0; index < pushedArrayLength; index++) {
           targetArray.push(pushedArray[index]);
       }
   }
}

1
你知道这个问题是由splice.apply引起的,还是它只出现在特定的浏览器中? - Drenai
1
@Drenai,这是一个每个浏览器的限制,你可以很容易地在大数组中达到它,就像这里提到的那样。 - Gabriel Kohen

0

-1

我想要一个函数,它只会取源数组的一部分,所以我根据CMS的答案稍微改了一下我的函数。

function spliceArray(array, index, howmany, source, start, end) {
    var arguments;
  if( source !== undefined ){
    arguments = source.slice(start, end);
    arguments.splice(0,0, index, howmany);
  } else{
   arguments = [index, howmany];
  }
    return Array.prototype.splice.apply(array, arguments)
}

Array.prototype.spliceArray = function(index, howmany, source, start, end) {
    return spliceArray(this, index, howmany, source, start, end);
}

您可以在此链接中查看:https://jsfiddle.net/matthewvukomanovic/nx858uz5/


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