首页 文章

按值复制数组

提问于
浏览
1456

将JavaScript中的数组复制到另一个数组时:

var arr1 = ['a','b','c'];
var arr2 = arr1;
arr2.push('d');  //Now, arr1 = ['a','b','c','d']

我意识到 arr2 指的是与 arr1 相同的数组,而不是一个新的独立数组 . 如何复制数组以获得两个独立的数组?

29 回答

  • 4

    使用jQuery深层复制可以如下:

    var arr2 = $.extend(true, [], arr1);
    
  • 2

    在Javascript中,深度复制技术依赖于数组中的元素 .
    我们从那里开始 .

    三种类型的元素

    元素可以是:文字值,文字结构或原型 .

    // Literal values (type1)
    const booleanLiteral = true;
    const numberLiteral = 1;
    const stringLiteral = 'true';
    
    // Literal structures (type2)
    const arrayLiteral = [];
    const objectLiteral = {};
    
    // Prototypes (type3)
    const booleanPrototype = new Bool(true);
    const numberPrototype = new Number(1);
    const stringPrototype = new String('true');
    const arrayPrototype = new Array();
    const objectPrototype = new Object(); # or "new function () {}"
    

    从这些元素中我们可以创建三种类型的数组 .

    // 1) Array of literal-values (boolean, number, string) 
    const type1 = [true, 1, "true"];
    
    // 2) Array of literal-structures (array, object)
    const type2 = [[], {}];
    
    // 3) Array of prototype-objects (function)
    const type3 = [function () {}, function () {}];
    

    深度复制技术取决于三种数组类型

    根据数组中元素的类型,我们可以使用各种技术进行深度复制 .

    • Array of literal-values (type1)
      [...myArray]myArray.splice(0)myArray.slice()myArray.concat() 技术可用于仅使用文字值(布尔值,数字和字符串)深度复制数组; Spread运算符 [...myArray] 具有最佳性能(https://measurethat.net/Benchmarks/Show/4281/0/spread-array-performance-vs-slice-splice-concat) .

    • Array of literal-values (type1) and literal-structures (type2)
      JSON.parse(JSON.stringify(myArray)) 技术可用于深层复制文字值(布尔值,数字,字符串)和文字结构(数组,对象),但不能用于原型对象 .

    • All arrays (type1, type2, type3)
      jQuery $.extend(myArray) 技术可用于深度复制所有数组类型 . 像UnderscoreLo-dash这样的库提供与jQuery $.extend() 类似的深拷贝函数,但性能较低 . 更令人惊讶的是, $.extend() 的性能高于 JSON.parse(JSON.stringify(myArray)) 技术http://jsperf.com/js-deep-copy/15 .
      对于那些回避第三方库(如jQuery)的开发人员,您可以使用以下自定义函数;它具有比$ .extend更高的性能,并深度复制所有数组 .

    function copy(aObject) {
      if (!aObject) {
        return aObject;
      }
    
      let v;
      let bObject = Array.isArray(aObject) ? [] : {};
      for (const k in aObject) {
        v = aObject[k];
        bObject[k] = (typeof v === "object") ? copy(v) : v;
      }
    
      return bObject;
    }
    

    所以回答这个问题......

    Question

    var arr1 = ['a','b','c'];
    var arr2 = arr1;
    

    我意识到arr2引用的是与arr1相同的数组,而不是一个新的独立数组 . 如何复制数组以获得两个独立的数组?

    Answer

    因为 arr1 是一个文字值数组(布尔值,数字或字符串),所以您可以使用上面讨论的任何深度复制技术,其中扩展运算符 ... 具有最高性能 .

    // Highest performance for deep copying literal values
    arr2 = [...arr1];
    
    // Any of these techniques will deep copy literal values as well,
    //   but with lower performance.
    arr2 = arr1.slice();
    arr2 = arr1.splice(0);
    arr2 = arr1.concat();
    arr2 = JSON.parse(JSON.stringify(arr1));
    arr2 = $.extend(true, [], arr1); // jQuery.js needed
    arr2 = _.extend(arr1); // Underscore.js needed
    arr2 = _.cloneDeep(arr1); // Lo-dash.js needed
    arr2 = copy(arr1); // Custom-function needed - as provided above
    
  • 2

    如果要创建对象或数组的新副本,则必须显式复制对象的属性或数组的元素,例如:

    var arr1 = ['a','b','c'];
    var arr2 = [];
    
    for (var i=0; i < arr1.length; i++) {
       arr2[i] = arr1[i];
    }
    

    您可以在Google上搜索有关不可变原始值和可变对象引用的更多信息 .

  • 408

    正如我们在Javascript arraysobjects 中所知的那样,但是我们可以通过什么方式复制数组而不需要在以后更改原始数组呢?

    以下是几种方法:

    想象一下,我们在您的代码中有这个数组:

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

    1)在函数中循环遍历数组并返回一个新数组,如下所示:

    function newArr(arr) {
          var i=0, res = [];
          while(i<arr.length){
           res.push(arr[i]);
            i++;
           }
       return res;
     }
    

    2)使用切片方法,切片用于切片数组的一部分,它将切片数组的某些部分而不接触原始数据,在切片中,如果不指定数组的开始和结束,它将切片整个数组和基本上是数组的完整副本,所以我们可以很容易地说:

    var arr2 = arr.slice(); // make a copy of the original array
    

    3)也是联系方法,这是为了合并两个数组,但是我们可以只指定一个数组,然后这基本上是新联系数组中的值的副本:

    var arr2 = arr.concat();
    

    4)也是字符串化和解析方法,不推荐,但可以是一种复制数组和对象的简单方法:

    var arr2 = JSON.parse(JSON.stringify(arr));
    

    5)Array.from方法,这个没有被广泛支持,在使用之前检查不同浏览器中的支持:

    const arr2 = Array.from(arr);
    

    6)ECMA6方式,也没有完全支持,但如果你想要转换,babelJs可以帮助你:

    const arr2 = [...arr];
    
  • 2

    用这个:

    var newArray = oldArray.slice();
    

    基本上,slice()操作克隆数组并返回对新数组的引用 . 另请注意:

    对于引用,字符串和数字(而不是实际对象), slice() 将对象引用复制到新数组中 . 原始数组和新数组都引用相同的对象 . 如果引用的对象发生更改,则更改对新的和原始数组都可见 .

    字符串和数字等原语是不可变的,因此不可能更改字符串或数字 .

  • 4

    丹,不需要使用花哨的技巧 . 你需要做的就是通过这样做复制arr1 .

    var arr2 = new Array(arr1);
    

    现在 arr1arr2 是存储在不同堆栈中的两个不同的数组变量 . Check this out on jsfiddle .

  • 1

    当我们想要使用赋值运算符( = )复制数组时,它不创建副本,它只是复制指向数组的指针/引用 . 例如:

    const oldArr = [1,2,3];
    
    const newArr = oldArr;  // now oldArr points to the same place in memory 
    
    console.log(oldArr === newArr);  // Points to the same place in memory thus is true
    
    const copy = [1,2,3];
    
    console.log(copy === newArr);  // Doesn't point to the same place in memory and thus is false
    

    通常,当我们转换数据时,我们希望保持初始数据结构(例如数组)不变 . 我们通过制作数组的精确副本来完成此操作,以便在初始数组保持不变的情况下对其进行转换 .

    复制数组的方法:

    const oldArr = [1,2,3];
    
    // Uses the spread operator to spread out old values into the new array literal
    const newArr1 = [...oldArr];
    
    // Slice with no arguments returns the newly copied Array
    const newArr2 = oldArr.slice();
    
    // Map applies the callback to every element in the array and returns a new array
    const newArr3 = oldArr.map((el) => el);
    
    // Concat is used to merge arrays and returns a new array. Concat with no args copies an array
    const newArr4 = oldArr.concat();
    
    // Object.assign can be used to transfer all the properties into a new array literal
    const newArr5 = Object.assign([], oldArr);
    
    // Creating via the Array constructor using the new keyword
    const newArr6 = new Array(...oldArr);
    
    // For loop
    function clone(base) {
    	const newArray = [];
        for(let i= 0; i < base.length; i++) {
    	    newArray[i] = base[i];
    	}
    	return newArray;
    }
    
    const newArr7 = clone(oldArr);
    
    console.log(newArr1, newArr2, newArr3, newArr4, newArr5, newArr6, newArr7);
    

    嵌套数组或对象时要小心!:

    嵌套数组时,将通过引用复制值 . 以下是一个如何导致问题的示例:

    let arr1 = [1,2,[1,2,3]]
    
    let arr2 = [...arr1];
    
    arr2[2][0] = 5;  // we change arr2
    
    console.log(arr1);  // arr1 is also changed because the array inside arr1 was copied by reference
    

    因此,当有对象或时,请不要使用这些方法要复制的数组中的数组 . 即仅在基元数组上使用这些方法 .

    如果你想深度克隆一个javascript数组,请使用 JSON.parseJSON.stringify ,如下所示:

    let arr1 = [1,2,[1,2,3]]
    
    let arr2 = JSON.parse(JSON.stringify(arr1)) ;
    
    arr2[2][0] = 5;
    
    console.log(arr1);  // now I'm not modified because I'm a deep clone
    

    复制表现:

    那么我们选择哪一个以获得最佳性能 . 事实证明,最详细的方法, for 循环具有最高的性能 . 使用 for 循环进行真正的CPU密集型复制(大型/多个阵列) .

    之后, .slice() 方法也具有不错的性能,并且程序员实现起来也不那么冗长和容易 . 我建议使用 .slice() 进行日常复制不太占用CPU的阵列 . 如果不需要深度克隆并且性能是个问题,也可以避免使用 JSON.parse(JSON.stringify(arr)) (大量开销) .

    Source performance test

  • 161

    这里有几种复制方式:

    const array = [1,2,3,4];
    
    const arrayCopy1 = Object.values(array);
    const arrayCopy2 = Object.assign([], array);
    const arrayCopy3 = array.map(i => i);
    const arrayCopy4 = Array.of(...array );
    
  • 14

    Important!

    这里的大多数答案适用于 particular cases .

    如果你不关心深层/嵌套对象和道具使用( ES6 ):

    let clonedArray = [...array]

    但如果你想做深度克隆,请改用:

    let cloneArray = JSON.parse(JSON.stringify(array))


    For lodash users:

    let clonedArray = _.clone(array) documentation

    let clonedArray = _.cloneDeep(array) documentation

  • 2310

    这是一个变种:

    var arr1=['a', 'b', 'c'];
    var arr2=eval(arr1.toSource());
    arr2.push('d');
    console.log('arr1: '+arr1+'\narr2: '+arr2);
    /*
     *  arr1: a,b,c
     *  arr2: a,b,c,d
     */
    
  • 1

    在处理数字或字符串等简单数据类型时,一些提到的方法很有效,但是当数组包含其他对象时,这些方法会失败 . 当我们尝试将任何对象从一个数组传递到另一个数组时,它将作为引用而不是对象传递 .

    在JavaScript文件中添加以下代码:

    Object.prototype.clone = function() {
        var newObj = (this instanceof Array) ? [] : {};
        for (i in this) {
            if (i == 'clone') 
                continue;
            if (this[i] && typeof this[i] == "object") {
                newObj[i] = this[i].clone();
            } 
            else 
                newObj[i] = this[i]
        } return newObj;
    };
    

    简单地使用

    var arr1 = ['val_1','val_2','val_3'];
    var arr2 = arr1.clone()
    

    它会工作 .

  • 11

    您可以通过以下方式执行此操作:
    arr2 = arr1.map(x => Object.assign({}, x));

  • 6

    不需要jQuery ... Working Example

    var arr2 = arr1.slice()
    

    这会将数组从起始位置 0 复制到数组的末尾 .

    重要的是要注意它将按原始类型(字符串,数字等)的预期工作,并且还解释引用类型的预期行为......

    如果您有一个引用类型数组,请输入 Object 类型 . 要复制数组 will ,但两个数组都将包含对同一 Object 的引用 . 所以在这种情况下,即使数组 is actually copied. 看起来像是通过引用复制数组

  • 147

    在我的特殊情况下,我需要确保阵列保持完整,所以这对我有用:

    // Empty array
    arr1.length = 0;
    // Add items from source array to target array
    for (var i = 0; i < arr2.length; i++) {
        arr1.push(arr2[i]);
    }
    
  • 14

    我个人认为Array.from是一个更具可读性的解决方案 . 顺便说一下,只要小心它的浏览器支持 .

    //clone
    let x = [1,2,3];
    let y = Array.from(x);
    
    //deep clone
    let clone = arr => Array.from(arr,item => Array.isArray(item) ? clone(item) : item);
    let x = [1,[],[[]]];
    let y = clone(x);
    
  • 9
    let a = [1,2,3];
    

    现在,您可以执行以下任一操作来制作数组的副本 .

    let b = Array.from(a);
    

    要么

    let b = new Array(...a);
    

    要么

    let b = a.slice();
    

    要么

    let b = a.map(e => e);
    

    现在,如果我改变了,

    a.push(5);
    

    然后,a是[1,2,3,5],但b仍然是[1,2,3],因为它有差异参考 .

    But i think, in all the methods above Array.from is better and made mainly to copy an array.

  • 2

    制作多维数组/对象的副本:

    function deepCopy(obj) {
       if (Object.prototype.toString.call(obj) === '[object Array]') {
          var out = [], i = 0, len = obj.length;
          for ( ; i < len; i++ ) {
             out[i] = arguments.callee(obj[i]);
          }
          return out;
       }
       if (typeof obj === 'object') {
          var out = {}, i;
          for ( i in obj ) {
             out[i] = arguments.callee(obj[i]);
          }
          return out;
       }
       return obj;
    }
    

    感谢James Padolsey的这项功能 .

    资料来源:Here

  • 68

    这是我在尝试了许多方法后的方法:

    var newArray = JSON.parse(JSON.stringify(orgArray));
    

    这将创建一个与第一个无关的新深拷贝(不是浅拷贝) .

    此外,这显然不会克隆事件和函数,但你可以在一行中做到这一点,它可以用于任何类型的对象(数组,字符串,数字,对象......)

  • 48

    如果您的数组包含 primitive data type 的元素,例如 int, char, or string etc ,那么您可以使用其中一种方法返回原始数组的副本,例如.slice()或.map()或spread运算符(感谢ES6) .

    new_array = old_array.slice()
    

    要么

    new_array = old_array.map((elem) => elem)
    

    要么

    const new_array = new Array(...old_array);
    

    BUT 如果你的数组包含 complex elements ,例如对象(或数组)或更多 nested objects ,那么,你必须确保你正在复制从顶层到最后一层的所有元素,否则内部对象的引用将会使用,这意味着更改new_array中object_elements中的值仍将影响old_array . 您可以在每个级别调用此复制方法作为old_array的 DEEP COPY .

    对于深度复制,您可以根据数据类型将上述方法用于每个级别的原始数据类型,或者您可以使用此 costly method(mentioned below) 进行深层复制而无需做太多工作 .

    var new_array = JSON.parse(JSON.stringify(old_array));
    

    根据您的要求,您可以使用许多其他方法 . 我只提到了其中的一些内容,以便大致了解当我们尝试将数组复制到另一个_132214中时会发生什么 .

  • 18

    有新推出的 Array.from ,但不幸的是,截至撰写本文时,它仅支持最近的Firefox版本(32及更高版本) . 它可以简单地使用如下:

    var arr1 = [1, 2, 3];
    console.log(Array.from(arr1)); // Logs: [1, 2, 3]
    

    参考:Here

    或者 Array.prototype.map 可以与身份功能一起使用:

    function identity(param)
    {
        return param;
    }
    
    var arr1 = [1, 2, 3],
        clone = arr1.map(identity);
    

    参考:Here

  • 11

    您还可以使用ES6扩展运算符来复制Array

    var arr=[2,3,4,5];
    var copyArr=[...arr];
    
  • 2

    从ES2015开始,

    var arr2 = [...arr1];
    
  • 2

    slice的替代是concat,可以以两种方式使用 . 其中第一个可能更具可读性,因为预期的行为非常明确:

    var array2 = [].concat(array1);
    

    第二种方法是:

    var array2 = array1.concat();
    

    科恩(在评论中)指出后一种方法has better performance .

    这种方法的工作方式是 concat 方法创建一个新数组,该数组由调用它的对象中的元素组成,后跟作为参数传递给它的任何数组的元素 . 因此,当没有传递参数时,它只是复制数组 .

    Lee Penkman也在评论中指出,如果有机会 array1undefined ,你可以返回一个空数组,如下所示:

    var array2 = [].concat(array1 || []);
    

    或者,对于第二种方法:

    var array2 = (array1 || []).concat();
    

    请注意,您也可以使用 slicevar array2 = (array1 || []).slice(); 执行此操作 .

  • 1

    对于包含对象的ES6数组

    cloneArray(arr) {
        return arr.map(x => ({ ...x }));
    }
    
  • 4

    你可以使用带有传播Opeartor的ES6,它更简单 .

    arr2 = [...arr1];
    

    有限制..检查文档Spread syntax @ mozilla

  • 1

    添加到 array.slice(); 的解决方案请注意,如果您有 multidimensional array 子数组将被引用复制 . 你可以做的是分别循环和切片()每个子数组

    var arr = [[1,1,1],[2,2,2],[3,3,3]];
    var arr2 = arr.slice();
    
    arr2[0][1] = 55;
    console.log(arr2[0][1]);
    console.log(arr[0][1]);
    
    function arrCpy(arrSrc, arrDis){
     for(elm in arrSrc){
      arrDis.push(arrSrc[elm].slice());
    }
    }
    
    var arr3=[];
    arrCpy(arr,arr3);
    
    arr3[1][1] = 77;
    
    console.log(arr3[1][1]);
    console.log(arr[1][1]);
    

    同样的东西转到对象数组,它们将通过引用复制,你必须手动复制它们

  • 0

    如果您在ECMAScript 6的环境中,使用Spread Operator可以这样做:

    var arr1 = ['a','b','c'];
    var arr2 = [...arr1]; //copy arr1
    arr2.push('d');
    
    console.log(arr1)
    console.log(arr2)
    
    <script src="http://www.wzvang.com/snippet/ignore_this_file.js"></script>
    
  • 3

    Quick Examples:

    • 如果数组中的元素是 primitive types (字符串,数字等)
    var arr1 = ['a','b','c'];
    // arr1 and arr2 are independent and primitive elements are stored in 
    // different places in the memory
    var arr2 = arr1.slice(); 
    arr2.push('d');
    console.log(arr1); // [ 'a', 'b', 'c' ]
    console.log(arr2); // [ 'a', 'b', 'c', 'd' ]
    
    • 如果数组中的元素是 object literals, another array ({}, [])
    var arr1 = [{ x: 'a', y: 'b'}, [1, 2], [3, 4]];
    // arr1 and arr2 are independent and reference's/addresses are stored in different
    // places in the memory. But those reference's/addresses points to some common place
    // in the memory.
    var arr2 = arr1.slice(); 
    arr2.pop();      // OK - don't affect arr1 bcos only the address in the arr2 is
                     // deleted not the data pointed by that address
    arr2[0].x = 'z'; // not OK - affect arr1 bcos changes made in the common area 
                     // pointed by the addresses in both arr1 and arr2
    arr2[1][0] = 9;	 // not OK - same above reason
    
    console.log(arr1); // [ { x: 'z', y: 'b' }, [ 9, 2 ], [ 3, 4 ] ]
    console.log(arr2); // [ { x: 'z', y: 'b' }, [ 9, 2 ] ]
    
    • Solution for 2 :按元素逐个深层复制
    var arr1 = [{ x: 'a', y: 'b'}, [1, 2], [3, 4]];
    arr2 = JSON.parse(JSON.stringify(arr1));
    arr2.pop();	  // OK - don't affect arr1
    arr2[0].x = 'z';  // OK - don't affect arr1
    arr2[1][0] = 9;	  // OK - don't affect arr1
    
    console.log(arr1); // [ { x: 'a', y: 'b' }, [ 1, 2 ], [ 3, 4 ] ]
    console.log(arr2); // [ { x: 'z', y: 'b' }, [ 9, 2 ] ]
    
  • 0

    您可以使用数组传播 ... 来复制数组 .

    const itemsCopy = [...items];

    此外,如果想要创建一个新数组,其中现有数组是其中的一部分:

    var parts = ['shoulders', 'knees'];
    var lyrics = ['head', ...parts, 'and', 'toes'];
    

    数组传播现在是supported in all major browsers但是如果你需要较旧的支持,请使用typescript或babel并编译为ES5 .

    More info on spreads

相关问题