代码之家  ›  专栏  ›  技术社区  ›  Marco Demaio

在JavaScript-slice与'for'循环中复制数组的最快方法

  •  545
  • Marco Demaio  · 技术社区  · 15 年前

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

    切片法

    var dup_array = original_array.slice();
    

    For

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

    我知道这两种方法只能 :如果原始数组包含对对象的引用,则不会克隆对象,但只复制引用,因此两个数组都将引用相同的对象。 但这不是这个问题的重点。

    16 回复  |  直到 7 年前
        1
  •  765
  •   user3145669 Dan    7 年前

    至少有 5个

    • 环
    • 数组.from()
    • 康卡

    有一个呼格 BENCHMARKS thread ,提供以下信息:

    • 眨眼 浏览器 slice() 是最快的方法, concat() 有点慢,而且 while loop

    • 对于其他浏览器 while循环 是最快的方法,因为这些浏览器没有对 slice 和 concat

    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);
    

    片

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

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

    origAr == clonedArr //returns false
    origAr[0] == clonedArr[0] //returns true
    
        2
  •  237
  •   Peter Mortensen Pieter Jan Bonestroo    7 年前

    技术上 slice 最快的方法。 然而 0 开始索引。

    myArray.slice(0);
    

    myArray.slice();
    

    http://jsperf.com/cloning-arrays/3

        3
  •  132
  •   Yukulélé    7 年前

    es6路怎么样?

    arr2 = [...arr1];
    
        4
  •  43
  •   Vladimir Kharlampidi    12 年前

    var dup_array = JSON.parse(JSON.stringify(original_array))
    
        5
  •  29
  •   Sajjad Shirazi    9 年前
    var cloned_array = [].concat(target_array);
    
        6
  •  26
  •   Peter Mortensen Pieter Jan Bonestroo    7 年前

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

    我在InternetExplorer8上的结果是156782和750,这表明 slice 在这种情况下要快得多。

        7
  •  20
  •   Peter Mortensen Pieter Jan Bonestroo    7 年前

    a.map(e => e) .map() 非常快(几乎和 .slice(0) )在Firefox中,但在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));
        8
  •  16
  •   Lior Elrom    6 年前

    克隆阵列的最快方法

    我制作了这个非常简单的实用程序函数来测试克隆一个数组所需的时间。它不是100%可靠的,但它可以让您大致了解克隆现有阵列所需的时间:

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

    并测试了不同的方法:

    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));
    

    更新 :
    注意:其中,深入克隆阵列的唯一方法是使用 JSON.parse(JSON.stringify(arr)) .

    null .
    感谢@GilEpshtain提供此更新 .

        9
  •  7
  •   Margus    15 年前

    link . 不是速度,而是舒适。除此之外,你只能使用 切片(0) 在 基本类型

    要制作数组的独立副本而不是引用的副本,可以使用array slice方法。

    例子:

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

    复制或克隆对象:

    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);
    

    link

        10
  •  7
  •   serv-inc    8 年前

    正如@Dan所说,“这个答案很快就过时了。使用 benchmarks 虽然

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

    每秒960589次 a.concat() 每秒578129次,占60%。


    @亚历克拉森创造了一个新的、更可靠的基准。

        11
  •  6
  •   Peter Mortensen Pieter Jan Bonestroo    7 年前

    Array.prototype.slice vs manual array creation ,有一个粗略的性能指南:

    Enter image description here

    结果:

    Enter image description here

        12
  •  6
  •   Peter Mortensen Pieter Jan Bonestroo    7 年前

    有一个更干净的解决方案:

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

    Array 构造函数在使用一个参数调用时的行为不同。

        13
  •  6
  •   Peter Mortensen Pieter Jan Bonestroo    7 年前

    记住,slice()对二维数组不起作用。您需要这样一个函数:

    function copy(array) {
      return array.map(function(arr) {
        return arr.slice();
      });
    }
    
        14
  •  5
  •   Peter Mortensen Pieter Jan Bonestroo    7 年前

    这取决于数组的长度。如果数组长度为<=1000000,则 slice 和 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();
    

    片 方法和 方法所用时间大致相同(3-4ms,取决于随机数)。

    如果将原始数组的长度设置为10000000,则 片 康卡 方法需要20多毫秒。

        15
  •  5
  •   Peter Mortensen Pieter Jan Bonestroo    7 年前

    Spread 操作员:

    基本示例:

    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);
    

    工具书类

        16
  •  3
  •   Caio Santos    9 年前

    一个简单的解决方案:

    original = [1,2,3]
    cloned = original.map(x=>x)
    
        17
  •  2
  •   Anki    7 年前
            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);
    
        18
  •  1
  •   Zibri    7 年前

    基准时间!

    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秒。

    我的结果:

    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)

        19
  •  1
  •   DevLoverUmar    6 年前

    在JavaScript中快速复制数组的方法如下:

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

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

    #3: array1copy = array1.slice();

    如果数组对象包含一些JSON不可序列化的内容(函数、数字、正无穷等),则最好使用

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

        20
  •  0
  •   Shuvro    6 年前

    
    const array = [1, 2, 3, 4]
    
    const newArray = [...array]
    newArray.push(6)
    console.log(array)
    console.log(newArray)