在 JavaScript 中复制数组的最快方法 - 切片与“for”环形

发布于 2024-09-28 21:40:52 字数 434 浏览 10 评论 0 原文

为了在 JavaScript 中复制数组:使用以下哪一个更快?

Slice 方法

var dup_array = original_array.slice();

For 循环

for(var i = 0, len = original_array.length; i < len; ++i)
   dup_array[i] = original_array[i];

我知道这两种方法都只进行浅复制:如果 original_array 包含对对象、对象的引用不会被克隆,但只会复制引用,因此两个数组将具有对相同对象的引用。 但这不是这个问题的重点。

我只问速度。

In order to duplicate an array in JavaScript: Which of the following is faster to use?

Slice method

var dup_array = original_array.slice();

For loop

for(var i = 0, len = original_array.length; i < len; ++i)
   dup_array[i] = original_array[i];

I know both ways do only a shallow copy: if original_array contains references to objects, objects won't be cloned, but only the references will be copied, and therefore both arrays will have references to the same objects.
But this is not the point of this question.

I'm asking only about speed.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(27

喵星人汪星人 2024-10-05 21:40:53

如果您想要 JS 中的真实克隆对象/数组,其中包含所有属性和子对象的克隆引用:

export function clone(arr) {
    return JSON.parse(JSON.stringify(arr))
}

所有其他操作都不会创建克隆,因为它们只是更改根元素的基地址,而不是所包含对象的基地址。

除非您递归遍历对象树。

对于简单的副本,这些都可以。对于存储地址相关操作,我建议(在大多数其他情况下,因为这很快!)将类型转换为字符串,然后返回一个完整的新对象。

If you want a REAL cloned object/array in JS with cloned references of all attributes and sub-objects:

export function clone(arr) {
    return JSON.parse(JSON.stringify(arr))
}

ALL other operations do not create clones, because they just change the base address of the root element, not of the included objects.

Except you traverse recursive through the object-tree.

For a simple copy, these are OK. For storage address relevant operations I suggest (and in most all other cases, because this is fast!) to type convert into string and back in a complete new object.

眼泪淡了忧伤 2024-10-05 21:40:53

如果您正在使用切片,它用于从数组中复制元素并创建具有相同编号的克隆。元素或更少。的元素。

var arr = [1, 2, 3 , 4, 5];

function slc() {
  var sliced = arr.slice(0, 5);
// arr.slice(position to start copying master array , no. of items in new array)
  console.log(sliced);
}
slc(arr);

If you are taking about slice it is used to copy elements from an array and create a clone with same no. of elements or less no. of elements.

var arr = [1, 2, 3 , 4, 5];

function slc() {
  var sliced = arr.slice(0, 5);
// arr.slice(position to start copying master array , no. of items in new array)
  console.log(sliced);
}
slc(arr);

柏拉图鍀咏恒 2024-10-05 21:40:53

2024 年 5 月

Chrome

“在此处输入图像描述”

Firefox

从未完成此基准测试,这肯定是基准测试工具的问题。

https://jsben.ch/lO6C5

另外,structuralClone 现在可用,但速度非常慢 (3%)

May 2024

Chrome

enter image description here

Firefox

never finishes this benchmark, certainly an issue with the benchmarking tool.

https://jsben.ch/lO6C5

Also, structuredClone is now available which is very slow (3%)

一紙繁鸢 2024-10-05 21:40:52

至少有 6 (!) 种克隆数组的方法:

  • loop
  • slice
  • Array.from()
  • concat
  • 扩展语法(最快)
  • 映射 A.map(function(e){return e;});

已经有一个huuuge BENCHMARKS 线程,提供以下信息:

  • 对于 blink 浏览器slice() 是最快的方法,concat()< /code> 慢一点,而 while 循环 慢 2.4 倍。


  • 对于其他浏览器,while循环是最快的方法,因为这些浏览器没有针对sliceconcat进行内部优化。

2016 年 7 月仍然如此。

下面是一些简单的脚本,您可以将其复制粘贴到浏览器的控制台中并运行多次以查看图片。它们输出毫秒,越低越好。

while 循环

n = 1000*1000;
start = + new Date();
a = Array(n); 
b = Array(n); 
i = a.length;
while(i--) b[i] = a[i];
console.log(new Date() - start);

slice

n = 1000*1000;
start = + new Date();
a = Array(n); 
b = a.slice();
console.log(new Date() - start);

请注意,这些方法将克隆 Array 对象本身,但数组内容是通过引用复制的,而不是深度克隆。

origAr == clonedArr //returns false
origAr[0] == clonedArr[0] //returns true

There are at least 6 (!) ways to clone an array:

  • loop
  • slice
  • Array.from()
  • concat
  • spread syntax (FASTEST)
  • map A.map(function(e){return e;});

There has been a huuuge BENCHMARKS thread, providing following information:

  • for blink browsers slice() is the fastest method, concat() is a bit slower, and while loop is 2.4x slower.

  • for other browsers while loop is the fastest method, since those browsers don't have internal optimizations for slice and concat.

This remains true in Jul 2016.

Below are simple scripts that you can copy-paste into your browser's console and run several times to see the picture. They output milliseconds, lower is better.

while loop

n = 1000*1000;
start = + new Date();
a = Array(n); 
b = Array(n); 
i = a.length;
while(i--) b[i] = a[i];
console.log(new Date() - start);

slice

n = 1000*1000;
start = + new Date();
a = Array(n); 
b = a.slice();
console.log(new Date() - start);

Please note that these methods will clone the Array object itself, array contents however are copied by reference and are not deep cloned.

origAr == clonedArr //returns false
origAr[0] == clonedArr[0] //returns true
〆一缕阳光ご 2024-10-05 21:40:52

从技术上讲,切片是最快的方法。 但是,如果添加 0 开始索引,速度会更快。

myArray.slice(0);

更快

myArray.slice();

https://jsben.ch/F0SZ3

Technically slice is the fastest way. However, it is even faster if you add the 0 begin index.

myArray.slice(0);

is faster than

myArray.slice();

https://jsben.ch/F0SZ3

孤芳又自赏 2024-10-05 21:40:52

es6方式怎么样?

arr2 = [...arr1];

what about es6 way?

arr2 = [...arr1];
孤城病女 2024-10-05 21:40:52

深度克隆数组或对象的最简单方法:

var dup_array = JSON.parse(JSON.stringify(original_array))

Easiest way to deep clone Array or Object:

var dup_array = JSON.parse(JSON.stringify(original_array))
如日中天 2024-10-05 21:40:52

???? Fastest Way to Clone an Array

I made this very plain utility function to test the time that it takes to clone an array. It is not 100% reliable. However, it can give you a bulk idea as to how long it takes to clone an existing array:

function clone(fn) {
  const arr = [...Array(1000000)];
  console.time('timer');
  fn(arr);
  console.timeEnd('timer');
}

And tested different approaches:

1)   5.79ms -> clone(arr => Object.values(arr));
2)   7.23ms -> clone(arr => [].concat(arr));
3)   9.13ms -> clone(arr => arr.slice());
4)  24.04ms -> clone(arr => { const a = []; for (let val of arr) { a.push(val); } return a; });
5)  30.02ms -> clone(arr => [...arr]);
6)  39.72ms -> clone(arr => JSON.parse(JSON.stringify(arr)));
7)  99.80ms -> clone(arr => arr.map(i => i));
8) 259.29ms -> clone(arr => Object.assign([], arr));
9) Maximum call stack size exceeded -> clone(arr => Array.of(...arr));

UPDATES

  1. Tests were made back in 2018, so most likely, you'll get different results with current browsers.
  2. Out of these methods, the best way to deep clone an array is by using JSON.parse(JSON.stringify(arr))
  3. Do not use the above if your array might include functions as it will return null.
    Thank you @GilEpshtain for this update.
最佳男配角 2024-10-05 21:40:52
var cloned_array = [].concat(target_array);
var cloned_array = [].concat(target_array);
奢望 2024-10-05 21:40:52

我整理了一个快速演示: http://jsbin.com/agugo3/edit

我在互联网上的结果  ;Explorer 8 为 156、782 和 750,这表明 slice 在这种情况下要快得多。

I put together a quick demo: http://jsbin.com/agugo3/edit

My results on Internet Explorer 8 are 156, 782, and 750, which would indicate slice is much faster in this case.

乱世争霸 2024-10-05 21:40:52

a.map(e => e) 是这项工作的另一种选择。截至目前,.map() 在 Firefox 中速度非常快(几乎与 .slice(0) 一样快),但在 Chrome 中则不然。

另一方面,如果数组是多维的,由于数组是对象而对象是引用类型,因此切片或连接方法都无法解决问题......因此克隆数组的一种正确方法是发明 <代码>Array.prototype.clone()如下。

Array.prototype.clone = function(){
  return this.map(e => Array.isArray(e) ? e.clone() : e);
};

var arr = [ 1, 2, 3, 4, [ 1, 2, [ 1, 2, 3 ], 4 , 5], 6 ],
    brr = arr.clone();
brr[4][2][1] = "two";
console.log(JSON.stringify(arr));
console.log(JSON.stringify(brr));

a.map(e => e) is another alternative for this job. As of today .map() is very fast (almost as fast as .slice(0)) in Firefox, but not in Chrome.

On the other hand, if an array is multi-dimensional, since arrays are objects and objects are reference types, none of the slice or concat methods will be a cure... So one proper way of cloning an array is an invention of Array.prototype.clone() as follows.

Array.prototype.clone = function(){
  return this.map(e => Array.isArray(e) ? e.clone() : e);
};

var arr = [ 1, 2, 3, 4, [ 1, 2, [ 1, 2, 3 ], 4 , 5], 6 ],
    brr = arr.clone();
brr[4][2][1] = "two";
console.log(JSON.stringify(arr));
console.log(JSON.stringify(brr));

游魂 2024-10-05 21:40:52

克隆对象数组的最快方法是使用扩展运算符

var clonedArray=[...originalArray]
or
var clonedArray = originalArray.slice(0); //with 0 index it's little bit faster than normal slice()

但克隆数组内的对象仍将指向旧的内存位置。因此,对 clonedArray 对象的更改也会更改 orignalArray。因此,

var clonedArray = originalArray.map(({...ele}) => {return ele})

这不仅会创建新数组,还会将对象克隆到其中。

免责声明如果您正在使用嵌套对象,在这种情况下扩展运算符将作为浅克隆工作。那时最好使用

var clonedArray=JSON.parse(JSON.stringify(originalArray));

Fastest way to clone an Array of Objects will be using spread operator

var clonedArray=[...originalArray]
or
var clonedArray = originalArray.slice(0); //with 0 index it's little bit faster than normal slice()

but the objects inside that cloned array will still pointing at the old memory location. hence change to clonedArray objects will also change the orignalArray. So

var clonedArray = originalArray.map(({...ele}) => {return ele})

this will not only create new array but also the objects will be cloned to.

disclaimer if you are working with nested object in that case spread operator will work as SHALLOW CLONE. At that point better to use

var clonedArray=JSON.parse(JSON.stringify(originalArray));
神魇的王 2024-10-05 21:40:52

请查看:链接。重要的不是速度,而是舒适度。此外,如您所见,您只能在基本类型上使用slice(0)

要制作数组的独立副本而不是对其引用的副本,可以使用数组切片方法。

例子:

要制作数组的独立副本而不是对其引用的副本,可以使用数组切片方法。

var oldArray = ["mip", "map", "mop"];
var newArray = oldArray.slice();

复制或克隆对象:

函数cloneObject(源) {
    for (i 在源代码中) {
        if (typeof source[i] == '源') {
            this[i] = new cloneObject(source[i]);
        }
        别的{
            this[i] = 源[i];
  }
    }
}

var obj1= {bla:'blabla',foo:'foofoo',etc:'etc'};
var obj2=新的cloneObject(obj1);

来源:链接

Take a look at: link. It's not about speed, but comfort. Besides as you can see you can only use slice(0) on primitive types.

To make an independent copy of an array rather than a copy of the refence to it, you can use the array slice method.

Example:

To make an independent copy of an array rather than a copy of the refence to it, you can use the array slice method.

var oldArray = ["mip", "map", "mop"];
var newArray = oldArray.slice();

To copy or clone an object :

function cloneObject(source) {
    for (i in source) {
        if (typeof source[i] == 'source') {
            this[i] = new cloneObject(source[i]);
        }
        else{
            this[i] = source[i];
  }
    }
}

var obj1= {bla:'blabla',foo:'foofoo',etc:'etc'};
var obj2= new cloneObject(obj1);

Source: link

我早已燃尽 2024-10-05 21:40:52

使用 Spread 运算符的 ECMAScript 2015 方式:

基本示例:

var copyOfOldArray = [...oldArray]
var twoArraysBecomeOne = [...firstArray, ..seccondArray]

在浏览器控制台中尝试:

var oldArray = [1, 2, 3]
var copyOfOldArray = [...oldArray]
console.log(oldArray)
console.log(copyOfOldArray)

var firstArray = [5, 6, 7]
var seccondArray = ["a", "b", "c"]
var twoArraysBecomOne = [...firstArray, ...seccondArray]
console.log(twoArraysBecomOne);

参考文献

ECMAScript 2015 way with the Spread operator:

Basic examples:

var copyOfOldArray = [...oldArray]
var twoArraysBecomeOne = [...firstArray, ..seccondArray]

Try in the browser console:

var oldArray = [1, 2, 3]
var copyOfOldArray = [...oldArray]
console.log(oldArray)
console.log(copyOfOldArray)

var firstArray = [5, 6, 7]
var seccondArray = ["a", "b", "c"]
var twoArraysBecomOne = [...firstArray, ...seccondArray]
console.log(twoArraysBecomOne);

References

挽心 2024-10-05 21:40:52

正如@Dan所说“这个答案很快就会过时。使用基准来检查实际情况”, jsperf 有一个具体的答案,但它本身还没有答案: while:

var i = a.length;
while(i--) { b[i] = a[i]; }

had 960,589 ops/sec with the runnerup a.concat() at 578,129 ops/sec ,即 60%。

这是最新的 Firefox (40) 64 位。


@aleclarson 创建了一个新的、更可靠的基准。

As @Dan said "This answer becomes outdated fast. Use benchmarks to check the actual situation", there is one specific answer from jsperf that has not had an answer for itself: while:

var i = a.length;
while(i--) { b[i] = a[i]; }

had 960,589 ops/sec with the runnerup a.concat() at 578,129 ops/sec, which is 60%.

This is the lastest Firefox (40) 64 bit.


@aleclarson created a new, more reliable benchmark.

瘫痪情歌 2024-10-05 21:40:52

基准时间!

function log(data) {
  document.getElementById("log").textContent += data + "\n";
}

benchmark = (() => {
  time_function = function(ms, f, num) {
    var z = 0;
    var t = new Date().getTime();
    for (z = 0;
      ((new Date().getTime() - t) < ms); z++)
      f(num);
    return (z)
  }

  function clone1(arr) {
    return arr.slice(0);
  }

  function clone2(arr) {
    return [...arr]
  }

  function clone3(arr) {
    return [].concat(arr);
  }

  Array.prototype.clone = function() {
    return this.map(e => Array.isArray(e) ? e.clone() : e);
  };

  function clone4(arr) {
    return arr.clone();
  }


  function benchmark() {
    function compare(a, b) {
      if (a[1] > b[1]) {
        return -1;
      }
      if (a[1] < b[1]) {
        return 1;
      }
      return 0;
    }

    funcs = [clone1, clone2, clone3, clone4];
    results = [];
    funcs.forEach((ff) => {
      console.log("Benchmarking: " + ff.name);
      var s = time_function(2500, ff, Array(1024));
      results.push([ff, s]);
      console.log("Score: " + s);

    })
    return results.sort(compare);
  }
  return benchmark;
})()
log("Starting benchmark...\n");
res = benchmark();

console.log("Winner: " + res[0][0].name + " !!!");
count = 1;
res.forEach((r) => {
  log((count++) + ". " + r[0].name + " score: " + Math.floor(10000 * r[1] / res[0][1]) / 100 + ((count == 2) ? "% *winner*" : "% speed of winner.") + " (" + Math.round(r[1] * 100) / 100 + ")");
});
log("\nWinner code:\n");
log(res[0][0].toString());
<textarea rows="50" cols="80" style="font-size: 16; resize:none; border: none;" id="log"></textarea>

自您单击按钮后,基准测试将运行 10 秒。

我的结果:

Chrome(V8引擎):

1. clone1 score: 100% *winner* (4110764)
2. clone3 score: 74.32% speed of winner. (3055225)
3. clone2 score: 30.75% speed of winner. (1264182)
4. clone4 score: 21.96% speed of winner. (902929)

Firefox(SpiderMonkey引擎):

1. clone1 score: 100% *winner* (8448353)
2. clone3 score: 16.44% speed of winner. (1389241)
3. clone4 score: 5.69% speed of winner. (481162)
4. clone2 score: 2.27% speed of winner. (192433)

获奖者代码:

function clone1(arr) {
    return arr.slice(0);
}

获胜引擎:

SpiderMonkey (Mozilla/Firefox)

Benchmark time!

function log(data) {
  document.getElementById("log").textContent += data + "\n";
}

benchmark = (() => {
  time_function = function(ms, f, num) {
    var z = 0;
    var t = new Date().getTime();
    for (z = 0;
      ((new Date().getTime() - t) < ms); z++)
      f(num);
    return (z)
  }

  function clone1(arr) {
    return arr.slice(0);
  }

  function clone2(arr) {
    return [...arr]
  }

  function clone3(arr) {
    return [].concat(arr);
  }

  Array.prototype.clone = function() {
    return this.map(e => Array.isArray(e) ? e.clone() : e);
  };

  function clone4(arr) {
    return arr.clone();
  }


  function benchmark() {
    function compare(a, b) {
      if (a[1] > b[1]) {
        return -1;
      }
      if (a[1] < b[1]) {
        return 1;
      }
      return 0;
    }

    funcs = [clone1, clone2, clone3, clone4];
    results = [];
    funcs.forEach((ff) => {
      console.log("Benchmarking: " + ff.name);
      var s = time_function(2500, ff, Array(1024));
      results.push([ff, s]);
      console.log("Score: " + s);

    })
    return results.sort(compare);
  }
  return benchmark;
})()
log("Starting benchmark...\n");
res = benchmark();

console.log("Winner: " + res[0][0].name + " !!!");
count = 1;
res.forEach((r) => {
  log((count++) + ". " + r[0].name + " score: " + Math.floor(10000 * r[1] / res[0][1]) / 100 + ((count == 2) ? "% *winner*" : "% speed of winner.") + " (" + Math.round(r[1] * 100) / 100 + ")");
});
log("\nWinner code:\n");
log(res[0][0].toString());
<textarea rows="50" cols="80" style="font-size: 16; resize:none; border: none;" id="log"></textarea>

The benchmark will run for 10s since you click the button.

My results:

Chrome (V8 engine):

1. clone1 score: 100% *winner* (4110764)
2. clone3 score: 74.32% speed of winner. (3055225)
3. clone2 score: 30.75% speed of winner. (1264182)
4. clone4 score: 21.96% speed of winner. (902929)

Firefox (SpiderMonkey Engine):

1. clone1 score: 100% *winner* (8448353)
2. clone3 score: 16.44% speed of winner. (1389241)
3. clone4 score: 5.69% speed of winner. (481162)
4. clone2 score: 2.27% speed of winner. (192433)

Winner code:

function clone1(arr) {
    return arr.slice(0);
}

Winner engine:

SpiderMonkey (Mozilla/Firefox)

满身野味 2024-10-05 21:40:52

这取决于浏览器。如果您查看博客文章Array.prototype.slice 与手动创建数组,每个都有一个粗略的性能指南:

在此处输入图像描述

结果:

在此处输入图像描述

It depends on the browser. If you look in the blog post Array.prototype.slice vs manual array creation, there is a rough guide to performance of each:

Enter image description here

Results:

Enter image description here

孤芳又自赏 2024-10-05 21:40:52

有一种更简洁的解决方案:

var srcArray = [1, 2, 3];
var clonedArray = srcArray.length === 1 ? [srcArray[0]] : Array.apply(this, srcArray);

需要进行长度检查,因为 Array 构造函数在仅使用一个参数调用时的行为有所不同。

There is a much cleaner solution:

var srcArray = [1, 2, 3];
var clonedArray = srcArray.length === 1 ? [srcArray[0]] : Array.apply(this, srcArray);

The length check is required, because the Array constructor behaves differently when it is called with exactly one argument.

︶葆Ⅱㄣ 2024-10-05 21:40:52

请记住 .slice() 不适用于二维数组。你需要一个像这样的函数:

function copy(array) {
  return array.map(function(arr) {
    return arr.slice();
  });
}

Remember .slice() won't work for two-dimensional arrays. You'll need a function like this:

function copy(array) {
  return array.map(function(arr) {
    return arr.slice();
  });
}
ζ澈沫 2024-10-05 21:40:52

这取决于数组的长度。如果数组长度 <= 1,000,000,则 sliceconcat 方法花费的时间大致相同。但是,当您提供更广泛的范围时,concat 方法会获胜。

例如,尝试以下代码:

var original_array = [];
for(var i = 0; i < 10000000; i ++) {
    original_array.push( Math.floor(Math.random() * 1000000 + 1));
}

function a1() {
    var dup = [];
    var start = Date.now();
    dup = original_array.slice();
    var end = Date.now();
    console.log('slice method takes ' + (end - start) + ' ms');
}

function a2() {
    var dup = [];
    var start = Date.now();
    dup = original_array.concat([]);
    var end = Date.now();
    console.log('concat method takes ' + (end - start) + ' ms');
}

function a3() {
    var dup = [];
    var start = Date.now();
    for(var i = 0; i < original_array.length; i ++) {
        dup.push(original_array[i]);
    }
    var end = Date.now();
    console.log('for loop with push method takes ' + (end - start) + ' ms');
}

function a4() {
    var dup = [];
    var start = Date.now();
    for(var i = 0; i < original_array.length; i ++) {
        dup[i] = original_array[i];
    }
    var end = Date.now();
    console.log('for loop with = method takes ' + (end - start) + ' ms');
}

function a5() {
    var dup = new Array(original_array.length)
    var start = Date.now();
    for(var i = 0; i < original_array.length; i ++) {
        dup.push(original_array[i]);
    }
    var end = Date.now();
    console.log('for loop with = method and array constructor takes ' + (end - start) + ' ms');
}

a1();
a2();
a3();
a4();
a5();

如果将original_array 的长度设置为 1,000,000,则 slice 方法和 concat 方法花费的时间大约相同(3-4 毫秒,取决于随机数)。

如果将 Original_array 的长度设置为 10,000,000,则 slice 方法将花费超过 60 毫秒,而 concat 方法将花费超过 20 毫秒。

It depends on the length of the array. If the array length is <= 1,000,000, the slice and concat methods are taking approximately the same time. But when you give a wider range, the concat method wins.

For example, try this code:

var original_array = [];
for(var i = 0; i < 10000000; i ++) {
    original_array.push( Math.floor(Math.random() * 1000000 + 1));
}

function a1() {
    var dup = [];
    var start = Date.now();
    dup = original_array.slice();
    var end = Date.now();
    console.log('slice method takes ' + (end - start) + ' ms');
}

function a2() {
    var dup = [];
    var start = Date.now();
    dup = original_array.concat([]);
    var end = Date.now();
    console.log('concat method takes ' + (end - start) + ' ms');
}

function a3() {
    var dup = [];
    var start = Date.now();
    for(var i = 0; i < original_array.length; i ++) {
        dup.push(original_array[i]);
    }
    var end = Date.now();
    console.log('for loop with push method takes ' + (end - start) + ' ms');
}

function a4() {
    var dup = [];
    var start = Date.now();
    for(var i = 0; i < original_array.length; i ++) {
        dup[i] = original_array[i];
    }
    var end = Date.now();
    console.log('for loop with = method takes ' + (end - start) + ' ms');
}

function a5() {
    var dup = new Array(original_array.length)
    var start = Date.now();
    for(var i = 0; i < original_array.length; i ++) {
        dup.push(original_array[i]);
    }
    var end = Date.now();
    console.log('for loop with = method and array constructor takes ' + (end - start) + ' ms');
}

a1();
a2();
a3();
a4();
a5();

If you set the length of original_array to 1,000,000, the slice method and concat method are taking approximately the same time (3-4 ms, depending on the random numbers).

If you set the length of original_array to 10,000,000, then the slice method takes over 60 ms and the concat method takes over 20 ms.

治碍 2024-10-05 21:40:52

在 ES6 中,您可以简单地利用 Spread 语法

示例:

let arr = ['a', 'b', 'c'];
let arr2 = [...arr];

请注意,展开运算符会生成一个全新的数组,因此修改一个数组不会影响另一个数组。

例子:

arr2.push('d') // becomes ['a', 'b', 'c', 'd']
console.log(arr) // while arr retains its values ['a', 'b', 'c']

In ES6, you can simply utilize the Spread syntax.

Example:

let arr = ['a', 'b', 'c'];
let arr2 = [...arr];

Please note that the spread operator generates a completely new array, so modifying one won't affect the other.

Example:

arr2.push('d') // becomes ['a', 'b', 'c', 'd']
console.log(arr) // while arr retains its values ['a', 'b', 'c']
海的爱人是光 2024-10-05 21:40:52

我是从另一个关于浅克隆数组的问题重定向到这里的,在发现大多数链接要么死了、过时或损坏后,我想我会发布一个可以在自己的环境中运行的解决方案。

以下代码应该半准确地测量使用特定方式克隆数组所需的时间。您可以使用开发者控制台或 Node.JS 在浏览器中直接运行它。您始终可以在此处找到它的最新版本。

function benchTime(cycles, timeLimit, fnSetup, ...fnProcess) {
    function measureCycle(timeLimit, fn, args) {
        let tmp = null;
        let end, start = performance.now();
        let iterations = 0;

        // Run until we exceed the time limit for one cycle.
        do {
            tmp = fn.apply(null, args);
            end = performance.now();
            ++iterations;
        } while ((end - start) <= timeLimit);
        tmp = undefined;

        // Build a result object and return it.
        return {
            "iterations": iterations,
            "start": start,
            "end": end,
            "duration": end - start,
            "opsPerSec": (iterations / (end - start)) * 1000.0,
        };
    }

    console.log(`Measuring ${fnProcess.length} functions...`);
    let params = fnSetup();
    //console.log("Setup function returned:", params);

    // Perform this for every function passed.
    for (let fn of fnProcess) {
        let results = [];
        console.groupCollapsed(`${fn.name}: Running for ${cycles} cycles...`);
        // Perform this N times.
        for (let cycle = 0; cycle < cycles; cycle++) {
            let result = {
                "iterations": Number.NaN,
                "start": Number.NaN,
                "end": Number.NaN,
                "duration": Number.NaN,
                "opsPerSec": Number.NaN,
            };

            try {
                result = measureCycle(timeLimit, fn, params);
                results.push(result);
            } catch (ex) {
                console.error(`${fn.name}:`, ex);
                break;
            }

            console.log(`Cycle ${cycle}/${cycles}: ${result.iterations}, ${result.end - result.start}, ${result.opsPerSec} ops/s`);
        }

        // If we have more than 3 repeats, drop slowest and fastest as outliers.
        if (results.length > 3) {
            console.log("Dropping slowest and fastest result.");
            results = results.sort((a, b) => {
                return (a.end - a.start) > (b.end - b.start);
            }).slice(1);
            results = results.sort((a, b) => {
                return (a.end - a.start) < (b.end - b.start);
            }).slice(1);
        }
        console.groupEnd();

        // Merge all results for the final average.
        let iterations = 0;
        let totalTime = 0;
        let opsPerSecMin = +Infinity;
        let opsPerSecMax = -Infinity;
        let opsPerSec = 0;
        for (let result of results) {
            iterations += result.iterations;
            totalTime += result.duration;
            opsPerSec += result.opsPerSec;
            if (opsPerSecMin > result.opsPerSec) {
                opsPerSecMin = result.opsPerSec;
            }
            if (opsPerSecMax < result.opsPerSec) {
                opsPerSecMax = result.opsPerSec;
            }
        }
        let operations = opsPerSec / results.length; //iterations / totalTime;
        let operationVariance = opsPerSecMax - opsPerSecMin;
        console.log(`${fn.name}: ${(operations).toFixed(2)}±${(operationVariance).toFixed(2)} ops/s, ${iterations} iterations over ${totalTime} ms.`);
    }
    console.log("Done.");
}

function spread(arr) { return [...arr]; }
function spreadNew(arr) { return new Array(...arr); }
function arraySlice(arr) { return arr.slice(); }
function arraySlice0(arr) { return arr.slice(0); }
function arrayConcat(arr) { return [].concat(arr); }
function arrayMap(arr) { return arr.map(i => i); }
function objectValues(arr) { return Object.values(arr); }
function objectAssign(arr) { return Object.assign([], arr); }
function json(arr) { return JSON.parse(JSON.stringify(arr)); }
function loop(arr) { const a = []; for (let val of arr) { a.push(val); } return a; }

benchTime(
    10, 1000,
    () => {
        let arr = new Array(16384);
        for (let a = 0; a < arr.length; a++) { arr[a] = Math.random(); };
        return [arr];
    },
    spread,
    spreadNew,
    arraySlice,
    arraySlice0,
    arrayConcat,
    arrayMap,
    objectValues,
    objectAssign,
    json,
    loop
);

我已经运行了几个尺寸,但这里有 16384 个元素数据的副本:

Test NodeJS v20.12.2 Firefox v127.0b2 Edge 124.0.2478.97
spread 13.180±1.022 ops/ms 7.436±1.110 ops/ms 3.321±0.239 ops /ms
spreadNew 4.727±0.532 ops/ms 1.010±0.022 ops/ms 21.045±2.982 ops/ms
arraySlice 12.912±2.127 ops/ms 11046.737±237.575 ops/ms 494.359±32.726 ops/ms
arraySlice0 13 .192±0.477 操作/毫秒 10665.299±500.553操作数/毫秒 492.209±66.837 操作数/毫秒
arrayConcat 16.590±0.656 操作数/毫秒 7923.657±224.637 操作数/毫秒 476.975±112.053 操作数/毫秒
arrayMap 6.542±0.301 操作数/毫秒 32.960±3.743 操作数/毫秒 52. 127±3.472 ops/ms
对象值 4.339± 0.111 操作/毫秒 10840.392±619.567 操作/毫秒 115.369±3.217 操作/毫秒
objectAssign 0.270±0.013 操作/毫秒 10471.860±202.291 操作/毫秒 83.135±3.439 操作/毫秒
json 0.205±0.039 操作秒/毫秒 4.014±1.679 操作/毫秒 7.730± 0.319 ops/ms
循环 6.138±0.287 ops/ms 6.727±1.296 ops/ms 27.691±1.217 ops/ms

总体而言,以下情况似乎为真:

  • (arraySlice)array.slice(), (arraySlice0)array.slice(0) 和 (arrayConcat)[].concat(array) 速度相当快。 arraySlice 似乎速度更快。
  • 仅在基于 Chromium 的浏览器中,(objectAssign)Object.assign([], array) 在处理大量数组时速度更快,但会带来消除任何未定义条目的缺点。
  • 浅克隆数组的最糟糕方法是 (spreadNew)New Array(...array)、(arrayMap)array.map(i => i)、 (objectValues)Object.values(array), (objectAssign)Object.assign(array), (json)JSON.parse(JSON.stringify(array) , (loop)let arr = new Array(); for (let e of array) { arr.push(e); return arr };
  • Firefox 似乎正在优化 。在某种程度上,我没能找到一种让 Firefox 看起来正常的方法。

免责声明:这些结果来自我的个人系统,我保留在我的代码中犯错误的权利。

I was redirected here from another question about shallow cloning Arrays, and after finding out that most of the links are either dead, outdated, or broken, figured I'd post a solution that you can just run in your own environment.

The following code should semi-accurately measure how much time it takes to clone an array using specific ways. You can run it in your Browser directly using the Developer console or Node.JS. The latest version of it can always be found here.

function benchTime(cycles, timeLimit, fnSetup, ...fnProcess) {
    function measureCycle(timeLimit, fn, args) {
        let tmp = null;
        let end, start = performance.now();
        let iterations = 0;

        // Run until we exceed the time limit for one cycle.
        do {
            tmp = fn.apply(null, args);
            end = performance.now();
            ++iterations;
        } while ((end - start) <= timeLimit);
        tmp = undefined;

        // Build a result object and return it.
        return {
            "iterations": iterations,
            "start": start,
            "end": end,
            "duration": end - start,
            "opsPerSec": (iterations / (end - start)) * 1000.0,
        };
    }

    console.log(`Measuring ${fnProcess.length} functions...`);
    let params = fnSetup();
    //console.log("Setup function returned:", params);

    // Perform this for every function passed.
    for (let fn of fnProcess) {
        let results = [];
        console.groupCollapsed(`${fn.name}: Running for ${cycles} cycles...`);
        // Perform this N times.
        for (let cycle = 0; cycle < cycles; cycle++) {
            let result = {
                "iterations": Number.NaN,
                "start": Number.NaN,
                "end": Number.NaN,
                "duration": Number.NaN,
                "opsPerSec": Number.NaN,
            };

            try {
                result = measureCycle(timeLimit, fn, params);
                results.push(result);
            } catch (ex) {
                console.error(`${fn.name}:`, ex);
                break;
            }

            console.log(`Cycle ${cycle}/${cycles}: ${result.iterations}, ${result.end - result.start}, ${result.opsPerSec} ops/s`);
        }

        // If we have more than 3 repeats, drop slowest and fastest as outliers.
        if (results.length > 3) {
            console.log("Dropping slowest and fastest result.");
            results = results.sort((a, b) => {
                return (a.end - a.start) > (b.end - b.start);
            }).slice(1);
            results = results.sort((a, b) => {
                return (a.end - a.start) < (b.end - b.start);
            }).slice(1);
        }
        console.groupEnd();

        // Merge all results for the final average.
        let iterations = 0;
        let totalTime = 0;
        let opsPerSecMin = +Infinity;
        let opsPerSecMax = -Infinity;
        let opsPerSec = 0;
        for (let result of results) {
            iterations += result.iterations;
            totalTime += result.duration;
            opsPerSec += result.opsPerSec;
            if (opsPerSecMin > result.opsPerSec) {
                opsPerSecMin = result.opsPerSec;
            }
            if (opsPerSecMax < result.opsPerSec) {
                opsPerSecMax = result.opsPerSec;
            }
        }
        let operations = opsPerSec / results.length; //iterations / totalTime;
        let operationVariance = opsPerSecMax - opsPerSecMin;
        console.log(`${fn.name}: ${(operations).toFixed(2)}±${(operationVariance).toFixed(2)} ops/s, ${iterations} iterations over ${totalTime} ms.`);
    }
    console.log("Done.");
}

function spread(arr) { return [...arr]; }
function spreadNew(arr) { return new Array(...arr); }
function arraySlice(arr) { return arr.slice(); }
function arraySlice0(arr) { return arr.slice(0); }
function arrayConcat(arr) { return [].concat(arr); }
function arrayMap(arr) { return arr.map(i => i); }
function objectValues(arr) { return Object.values(arr); }
function objectAssign(arr) { return Object.assign([], arr); }
function json(arr) { return JSON.parse(JSON.stringify(arr)); }
function loop(arr) { const a = []; for (let val of arr) { a.push(val); } return a; }

benchTime(
    10, 1000,
    () => {
        let arr = new Array(16384);
        for (let a = 0; a < arr.length; a++) { arr[a] = Math.random(); };
        return [arr];
    },
    spread,
    spreadNew,
    arraySlice,
    arraySlice0,
    arrayConcat,
    arrayMap,
    objectValues,
    objectAssign,
    json,
    loop
);

I've run this for a few sizes, but here's a copy of the 16384 element data:

Test NodeJS v20.12.2 Firefox v127.0b2 Edge 124.0.2478.97
spread 13.180±1.022 ops/ms 7.436±1.110 ops/ms 3.321±0.239 ops/ms
spreadNew 4.727±0.532 ops/ms 1.010±0.022 ops/ms 21.045±2.982 ops/ms
arraySlice 12.912±2.127 ops/ms 11046.737±237.575 ops/ms 494.359±32.726 ops/ms
arraySlice0 13.192±0.477 ops/ms 10665.299±500.553 ops/ms 492.209±66.837 ops/ms
arrayConcat 16.590±0.656 ops/ms 7923.657±224.637 ops/ms 476.975±112.053 ops/ms
arrayMap 6.542±0.301 ops/ms 32.960±3.743 ops/ms 52.127±3.472 ops/ms
objectValues 4.339±0.111 ops/ms 10840.392±619.567 ops/ms 115.369±3.217 ops/ms
objectAssign 0.270±0.013 ops/ms 10471.860±202.291 ops/ms 83.135±3.439 ops/ms
json 0.205±0.039 ops/ms 4.014±1.679 ops/ms 7.730±0.319 ops/ms
loop 6.138±0.287 ops/ms 6.727±1.296 ops/ms 27.691±1.217 ops/ms

Overall, it appears as if the following is true:

  • (arraySlice)array.slice(), (arraySlice0)array.slice(0) and (arrayConcat)[].concat(array) are equivalently fast. arraySlice appears to be minimally faster.
  • In Chromium-based Browsers only, (objectAssign)Object.assign([], array) is faster with massive arrays, but brings the downside of eliminating any entries that are undefined.
  • The worst ways overall to shallow-clone arrays are (spreadNew)New Array(...array), (arrayMap)array.map(i => i), (objectValues)Object.values(array), (objectAssign)Object.assign(array), (json)JSON.parse(JSON.stringify(array), (loop)let arr = new Array(); for (let e of array) { arr.push(e); }; return arr; }.
  • Firefox appears to be optimizing in a way that outright looks like cheating. I've failed to identify a way to make Firefox look normal.

Disclaimer: These results are from my personal system, and I reserve the right to have made an error in my code.

春风十里 2024-10-05 21:40:52
        const arr = ['1', '2', '3'];

         // Old way
        const cloneArr = arr.slice();

        // ES6 way
        const cloneArrES6 = [...arr];

// But problem with 3rd approach is that if you are using muti-dimensional 
 // array, then only first level is copied

        const nums = [
              [1, 2], 
              [10],
         ];

        const cloneNums = [...nums];

// Let's change the first item in the first nested item in our cloned array.

        cloneNums[0][0] = '8';

        console.log(cloneNums);
           // [ [ '8', 2 ], [ 10 ], [ 300 ] ]

        // NOOooo, the original is also affected
        console.log(nums);
          // [ [ '8', 2 ], [ 10 ], [ 300 ] ]

因此,为了避免这些情况发生,请使用

        const arr = ['1', '2', '3'];

        const cloneArr = Array.from(arr);
        const arr = ['1', '2', '3'];

         // Old way
        const cloneArr = arr.slice();

        // ES6 way
        const cloneArrES6 = [...arr];

// But problem with 3rd approach is that if you are using muti-dimensional 
 // array, then only first level is copied

        const nums = [
              [1, 2], 
              [10],
         ];

        const cloneNums = [...nums];

// Let's change the first item in the first nested item in our cloned array.

        cloneNums[0][0] = '8';

        console.log(cloneNums);
           // [ [ '8', 2 ], [ 10 ], [ 300 ] ]

        // NOOooo, the original is also affected
        console.log(nums);
          // [ [ '8', 2 ], [ 10 ], [ 300 ] ]

So, in order to avoid these scenarios to happen, use

        const arr = ['1', '2', '3'];

        const cloneArr = Array.from(arr);
孤城病女 2024-10-05 21:40:52

克隆数组的方法有多种。基本上,克隆有两种分类方式:

  1. 浅复制
  2. 深复制

浅拷贝只覆盖数组的第一层,其余的都是
参考。如果您想要数组中嵌套元素的真实副本,则需要
深度克隆。

例子 :

const arr1 = [1,2,3,4,5,6,7]           
// Normal Array (shallow copy is enough)     
const arr2 = [1,2,3,[4],[[5]],6,7]          
// Nested Array  (Deep copy required) 


Approach 1 : Using (...)Spread Operator  (Shallow copy enough)
const newArray = [...arr1] // [1,2,3,4,5,6,7]

Approach 2 : Using Array builtIn Slice method (Deep copy)  
const newArray = arr1.slice()  // [1,2,3,4,5,6,7]

Approach 3 : Using Array builtIn Concat method (Deep a copy)
const newArray = [].concat(arr1)  // [1,2,3,4,5,6,7]

Approach 4 : Using JSON.stringify/parse. (Deep a copy & fastest)
const newArray = JSON.parse(JSON.stringify(arr2));)  // [1,2,3,[4],[[5]],6,7]

Approach 5: Using own recursive function or using loadash's __.cloneDeep method. (Deep copy)

There were several ways to clone an array. Basically, Cloning was categorized in two ways:

  1. Shallow copy
  2. Deep copy

Shallow copies only cover the 1st level of the array and the rest are
referenced. If you want a true copy of nested elements in the arrays, you’ll need a
deep clone.

Example :

const arr1 = [1,2,3,4,5,6,7]           
// Normal Array (shallow copy is enough)     
const arr2 = [1,2,3,[4],[[5]],6,7]          
// Nested Array  (Deep copy required) 


Approach 1 : Using (...)Spread Operator  (Shallow copy enough)
const newArray = [...arr1] // [1,2,3,4,5,6,7]

Approach 2 : Using Array builtIn Slice method (Deep copy)  
const newArray = arr1.slice()  // [1,2,3,4,5,6,7]

Approach 3 : Using Array builtIn Concat method (Deep a copy)
const newArray = [].concat(arr1)  // [1,2,3,4,5,6,7]

Approach 4 : Using JSON.stringify/parse. (Deep a copy & fastest)
const newArray = JSON.parse(JSON.stringify(arr2));)  // [1,2,3,[4],[[5]],6,7]

Approach 5: Using own recursive function or using loadash's __.cloneDeep method. (Deep copy)
献世佛 2024-10-05 21:40:52

一个简单的解决方案:

original = [1,2,3]
cloned = original.map(x=>x)

A simple solution:

original = [1,2,3]
cloned = original.map(x=>x)
赢得她心 2024-10-05 21:40:52

在 JavaScript 中按顺序复制数组的快速方法:

#1: array1copy = [...array1];

#2: array1copy = array1.slice(0);

#3: array1copy = array1.slice();

如果你数组对象包含一些 JSON 不可序列化的内容(函数、Number.POSITIVE_INFINITY 等)更好用

array1copy = JSON.parse(JSON.stringify(array1))

Fast ways to duplicate an array in JavaScript in Order:

#1: array1copy = [...array1];

#2: array1copy = array1.slice(0);

#3: array1copy = array1.slice();

If your array objects contain some JSON-non-serializable content (functions, Number.POSITIVE_INFINITY, etc.) better to use

array1copy = JSON.parse(JSON.stringify(array1))

抹茶夏天i‖ 2024-10-05 21:40:52

您可以按照此代码操作。不可变方式数组克隆。这是阵列克隆的完美方式


const array = [1, 2, 3, 4]

const newArray = [...array]
newArray.push(6)
console.log(array)
console.log(newArray)

You can follow this code. Immutable way array clone. This is the perfect way to array cloning


const array = [1, 2, 3, 4]

const newArray = [...array]
newArray.push(6)
console.log(array)
console.log(newArray)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文