값으로 배열 복사


1744

JavaScript의 배열을 다른 배열로 복사하는 경우 :

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

새로운 독립형 배열이 아닌와 arr2동일한 배열 을 참조 한다는 것을 깨달았습니다 arr1. 배열을 복사하여 두 개의 독립적 인 배열을 얻으려면 어떻게해야합니까?


3
현재 Chrome 53 및 Firefox 48에서는 멋진 성능 slicesplice운영 기능과 새로운 스프레드 연산자를 Array.from가지고 있으며 구현 속도가 훨씬 느립니다. perfjs.fnfo
Pencroff

jsben.ch/#/wQ9RU <=이 벤치 마크는 어레이를 복사하는 다양한 방법에 대한 개요를 제공합니다.
EscapeNetscape


이 배열 (원시 문자열을 포함하는 하나)를 들어 당신이 사용할 수있는 var arr2 = arr1.splice();딥 카피로하지만,이 기술은 배열의 요소는 문자 구조 (즉, 포함하지 작업하는 경우 []{}) 또는 프로토 타입 객체 (즉 function () {}, new등). 추가 솔루션은 아래 답변을 참조하십시오.
tfmontague

16
2017 년이므로 ES6 기능 사용을 고려할 수 있습니다. let arr2 = [...arr1]; developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Hinrich

답변:


2700

이것을 사용하십시오 :

var newArray = oldArray.slice();

기본적으로이 slice()작업은 배열을 복제하고 새 배열에 대한 참조를 반환합니다.

또한 다음을 참고하십시오.

참조, 문자열 및 숫자 (실제 객체는 아님)의 경우 slice()객체 참조를 새 배열에 복사합니다. 원래 배열과 새 배열은 모두 동일한 객체를 나타냅니다. 참조 된 객체가 변경되면 변경 사항이 새 배열과 원래 배열 모두에 표시됩니다.

문자열 및 숫자와 같은 기본 요소는 변경할 수 없으므로 문자열 또는 숫자를 변경할 수 없습니다.


9
성능과 관련하여 다음 jsPerf 테스트는 실제로 var arr2 = arr1.slice ()가 var arr2 = arr1.concat (); JSPerf : jsperf.com/copy-array-slice-vs-concat/5jsperf.com/copy-simple-array . jsperf.com/array-copy/5 의 결과 는 테스트 코드가 유효한지 궁금한 점에 놀랐습니다.
코헨

94
이것은 이미 많은 찬사를 받았지만 불행히도 드물지만 JS의 참조를 올바르게 설명하기 때문에 또 다른 가치가 있습니다.
Wayne

34
@ GáborI 쉽게 읽을 수 있도록 전체 라이브러리를 추가 하시겠습니까? 정말? 가독성에 관심이 있다면 의견을 추가하고 싶습니다. 참조 : var newArray = oldArray.slice (); // oldArray를 newArray로 복제
dudewad

12
@ GáborImre 나는 그것을 얻는다. 그러나 내 의견에 전체 라이브러리를 포함시켜 특정 엔지니어링 문제에 대답하는 것은 도움이되지 않습니다. 디자인이 부풀어 오른 것입니다. 개발자가 그렇게 많이하는 것을 보았습니다. 그러면 단일 기능을 작성 해야하는 대신 전체 프레임 워크가 포함 된 프로젝트가 끝납니다. 그래도 내 MO.
dudewad

5
수업은 배운 : 혼동하지 마십시오 .slice()과 함께 .splice(), 당신에게 하늘의 배열을 제공한다. 큰 차이.
crazypeter

530

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 () {}];

딥 카피 기술은 3 가지 어레이 유형에 따라 다릅니다.

배열의 요소 유형에 따라 다양한 기술을 사용하여 딥 카피 할 수 있습니다.

요소 유형별 Javascript 딥 카피 기술

  • 리터럴 값 (타입 1)의 배열 , , 및 기술은 단지 리터럴 값 (부울, 숫자 및 문자열) 깊은 복사 어레이에 사용될 수있다; Spread 연산자 가 최고의 성능을 발휘하는 곳 ( https://measurethat.net/Benchmarks/Show/4281/0/spread-array-performance-vs-slice-splice-concat ).
    [...myArray]myArray.splice(0)myArray.slice()myArray.concat()[...myArray]

  • 리터럴 값 (type1) 및 리터럴 구조 (type2)
    배열JSON.parse(JSON.stringify(myArray))기술을 사용하여 리터럴 값 (부울, 숫자, 문자열) 및 리터럴 구조 (배열, 객체)를 딥 카피 할 수 있지만 프로토 타입 객체는 사용할 수 없습니다.

  • 모든 배열 (type1, type2, type3)
    jQuery $.extend(myArray)기술을 사용하여 모든 배열 유형을 딥 카피 할 수 있습니다. UnderscoreLo-dash 와 같은 라이브러리는 jQuery 와 유사한 딥 카피 기능을 제공하지만 $.extend()성능은 떨어집니다. 놀랍게도 http://jsperf.com/js-deep-copy/15 기술 $.extend()보다 성능이 우수합니다 . jQuery와 같은 타사 라이브러리를 사용하지 않는 개발자의 경우 다음과 같은 사용자 정의 기능을 사용할 수 있습니다. $ .extend보다 성능이 높고 모든 어레이를 딥 카피합니다.JSON.parse(JSON.stringify(myArray))

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

질문에 대답하려면 ...

질문

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

arr2는 새로운 독립형 배열이 아니라 arr1과 동일한 배열을 나타냅니다. 배열을 복사하여 두 개의 독립적 인 배열을 얻으려면 어떻게해야합니까?

대답

때문에 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

1
이러한 접근 방식 중 다수는 제대로 작동하지 않습니다. 대입 연산자를 사용하면 원래 리터럴 값인을 다시 할당해야합니다 arr1. 그것이 사실이되는 것은 매우 드 rare니다. splice말살을 사용하면 arr1전혀 사본이 아닙니다. JSON배열의 값이 함수이거나 프로토 타입 (예 : a Date)이 있으면 사용 이 실패합니다 .
Dancrumb

스플 라이스 사용은 부분 솔루션입니다. JSON보다 훨씬 많은 경우 실패합니다. 스플 라이스는 값을 이동할 때 문자열과 숫자의 딥 카피를 생성합니다.
tfmontague

1
왜 접속 (0)? slice ()이어야하지 않습니까? 스플 라이스가 수행하는 원래 배열을 수정하지 않아야한다고 생각합니다. @JamesMontagne
15:29에

2
splice는 원래 배열의 요소에 대한 포인터를 생성합니다 (얕은 복사). splice (0)은 배열의 요소에 숫자 또는 문자열 인 새 메모리 (딥 카피)를 할당하고 다른 모든 요소 유형 (얕은 카피)에 대한 포인터를 만듭니다. 시작 값 0을 스플 라이스 함수 메서드에 전달하면 원래 배열의 요소를 스 플라이 싱하지 않으므로 수정하지 않습니다.
tfmontague

1
실제로 "어떤 것"의 배열은 한 가지 유형의 배열 만 있습니다. [0,"1",{2:3},function random() {return 4;}, [[5,6,7],[8,9,10],[11,12,13]]]다른 어레이와 는 차이가 없습니다 .
wizzwizz4

189

배열 스프레드 ...를 사용하여 배열 을 복사 할 수 있습니다 .

const itemsCopy = [...items];

또한 기존 배열의 일부인 새 배열을 만들려면 다음을 수행하십시오.

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

어레이 스프레드는 이제 모든 주요 브라우저에서 지원 되지만 이전 지원이 필요한 경우 typescript 또는 babel을 사용하고 ES5로 컴파일하십시오.

스프레드에 대한 추가 정보


1
딥 카피에는 작동하지 않습니다. JavaScript에서 배열을 딥 복제합니다 .
SNag

151

jQuery가 필요하지 않습니다 ... 작업 예

var arr2 = arr1.slice()

그러면 배열이 시작 위치에서 배열 끝까지 복사 0됩니다.

기본 유형 (문자열, 숫자 등)에 대해 예상대로 작동하고 참조 유형에 대해 예상되는 동작을 설명하는 것이 중요합니다 ...

참조 유형의 배열이있는 경우 유형이라고 말하십시오 Object. 배열 복사되지만 두 배열 모두에 대한 참조가 포함됩니다 Object. 따라서이 경우 배열 이 실제로 복사 되더라도 배열 참조로 복사되는 것처럼 보입니다 .


12
이것은 깊은 사본이 아닙니다.
jondavidjohn

이 시도; var arr2 = JSON.stringify(arr1); arr2 = JSON.parse(arr2);
pradeep1991singh

2
이 답변과 허용되는 답변의 차이점은 무엇입니까?
Isaac Pak

당신의 주어진 예를 들어 콘솔에서 오류가 "형식 오류 : window.addEvent는 함수가 아닙니다"
라비 샤르마

72

의 대안 sliceconcat2 가지 방법으로 사용할 수 있습니다. 의도 된 동작이 매우 명확하기 때문에 이들 중 첫 번째가 더 읽기 쉽습니다.

var array2 = [].concat(array1);

두 번째 방법은 다음과 같습니다.

var array2 = array1.concat();

Cohen (의견에서)은이 후자의 방법 이 더 나은 성능을 가지고 있다고 지적했다 .

이것이 작동하는 concat방법은 메소드가 호출되는 객체의 요소와 인수로 전달 된 배열의 요소로 구성된 새 배열을 작성하는 것입니다. 따라서 인수가 전달되지 않으면 단순히 배열을 복사합니다.

Lee Penkman은 또한 의견에서 기회 array1undefined있으면 다음과 같이 빈 배열을 반환 할 수 있다고 지적 합니다.

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

또는 두 번째 방법의 경우 :

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

다음과 slice같이 할 수도 있습니다 var array2 = (array1 || []).slice();.


31
실제로 다음을 수행 할 수도 있습니다. var array2 = array1.concat (); 성능면에서 훨씬 빠릅니다. (JSPerf : jsperf.com/copy-simple-arrayjsperf.com/copy-array-slice-vs-concat/5
Cohen

5
array1이 배열이 아닌 경우 예를 들어 정의되지 않은 경우 get 을 [].concat(array1)반환 한다는 점은 주목할 가치가 [array1]있습니다 [undefined]. 가끔 해요var array2 = [].concat(array1 || []);
lee penkman

60

이것은 많은 접근법을 시도한 후에 내가 한 일입니다.

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

이렇게하면 첫 번째 사본과 관련이없는 새 사본이 생성됩니다 (얕은 사본이 아님).

또한 이것은 분명히 이벤트와 함수를 복제하지는 않지만 한 줄로 할 수있는 좋은 방법이며 모든 종류의 객체 (배열, 문자열, 숫자, 객체 ...)에 사용할 수 있습니다


4
이것이 최고입니다. 나는 오래 전에 같은 방법을 사용하고 구식 재귀 루프에는 더 이상 의미가 없다고 생각합니다.
Vladimir Kharlampidi

1
이 옵션은 그래프와 같은 구조를 잘 처리하지 못합니다. 사이클이있을 때 충돌이 발생하고 공유 참조를 유지하지 않습니다.
Ruben

1
또한 Date프로토 타입이있는 것과 같은 경우 에도 실패합니다 . 또한 undefineds는 s로 변환됩니다 null.
Dancrumb

7
CPU와 메모리 모두 텍스트로 직렬화 한 다음 다시 객체로 구문 분석하는 데있어 총 비 효율성에 대해 언급 할만큼 용감한 사람이 없습니까?
Lawrence Dol

3
이 솔루션은 효과가있는 유일한 솔루션입니다. slice ()를 사용하는 것은 실제로 가짜 솔루션입니다.

20

언급 된 메소드 중 일부는 숫자 또는 문자열과 같은 간단한 데이터 유형으로 작업 할 때 잘 작동하지만 배열에 다른 오브젝트가 포함되어 있으면 이러한 메소드가 실패합니다. 한 배열에서 다른 배열로 객체를 전달하려고하면 객체가 아니라 참조로 전달됩니다.

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

작동합니다.


2
'catch되지 않은 오류 RangeError : 최대 호출 스택의 크기는 초과'내 페이지에이 코드를 추가 할 때이 오류를 얻을 수
sawe

1
사과합니다.이 오류는 arr1이 선언되지 않은 경우 크롬에서 발생합니다. 그래서 위의 코드를 복사하여 붙여 넣기하면 오류가 발생하지만 배열 arr1을 선언하면 오류가 발생하지 않습니다. arr2 바로 위에 arr1을 선언하여 답변을 향상시킬 수 있습니다. 우리가 arr1을 선언해야한다는 것을 인식하지 못한 '우리'가 꽤 있습니다. '그냥 작동하는 것'이 필요했습니다)
sawe

.slice()배열에 객체가 있어도 여전히 잘 작동합니다 : jsfiddle.net/edelman/k525g
Jason

7
@Jason 그러나 객체는 여전히 동일한 객체를 가리 키므로 하나를 변경하면 다른 객체가 변경됩니다. jsfiddle.net/k525g/1
Samuel

훌륭한 코드. 내가 가진 한 가지 질문은 실제로 다음과 같이 한 배열을 다른 배열에 복사하려고 시도했습니다. var arr1 = new Array () 그리고 var arr2 = arr1; arr2에서 무언가를 변경하면 arr1도 변경됩니다. 그러나 생성 한 복제 프로토 타입을 사용하면 실제로 해당 배열의 완전한 새 인스턴스를 생성합니다. 즉, 복사합니다. 이것이 브라우저 문제입니까? 또는 기본적으로 javascript는 누군가 var arr2 = arr1을 수행하고 정수 변수로 발생하지 않는 경우 포인터를 사용하여 다른 변수를 가리키는 두 변수를 설정합니다. jsfiddle.net/themhz/HbhtA
themhz


17

개인적으로 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);

1
예, 이것은 매우 읽기 쉽습니다. 이 .slice()솔루션은 완전히 직관적이지 않습니다. 고마워
Banago

15

중대한!

여기에있는 대부분의 답변은 특정 경우에 적용 됩니다.

깊거나 중첩 된 객체 및 소품에 신경 쓰지 않으면 ( ES6 )을 사용하십시오.

let clonedArray = [...array]

그러나 딥 클론을 원한다면 이것을 대신 사용하십시오 :

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


lodash 사용자의 경우 :

let clonedArray = _.clone(array) 선적 서류 비치

let clonedArray = _.cloneDeep(array) 선적 서류 비치



9

array.slice () 의 솔루션에 추가 다차원 배열을 사용 하는 경우 하위 배열이 참조로 복사됩니다. 할 수있는 일은 각 하위 배열을 개별적으로 반복하고 slice ()하는 것입니다

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

같은 것들이 객체의 배열에 간다, 그것들은 참조로 복사 될 것이고, 수동으로 복사해야합니다.


이 답변은 페이지 상단 부근에 자리가 있습니다! 나는 다차원 하위 ​​배열로 작업하고 있었고 왜 내부 배열이 항상 val이 아닌 ref로 복사되었는지 알 수 없었습니다. 이 간단한 논리는 내 문제를 해결했습니다. 가능하다면 +100을 줄 것입니다!
Mac

8

기본 값은 항상 해당 값 (복사)을 통과합니다. 그러나 복합 값은 참조로 전달됩니다.

이 arr을 어떻게 복사합니까?

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

ES6에서 어레이 복사

let arrCopy = [...arr]; 

ES5에서 n 배열 복사

let arrCopy = arr.slice(); 
let arrCopy = [].concat(arr);

`let arrCopy = arr`이 왜 값으로 전달되지 않습니까?

Object / Array와 같은 복합 값에서 하나의 변수를 다른 변수에 전달하면 다르게 작동합니다. copand 값에 부호 연산자를 사용하여 객체에 대한 참조를 전달합니다. arr 요소를 제거 / 추가 할 때 두 배열의 값이 변경되는 이유입니다.

예외 :

arrCopy[1] = 'adding new value this way will unreference';

변수에 새 값을 할당하면 참조 자체가 변경되며 원래 객체 / 배열에는 영향을 미치지 않습니다.

더 읽어보기


6

Javascript 배열객체 에서 알 수 있듯이 참조는 가능하지만 나중에 원래 배열을 변경하지 않고 배열을 복사 할 수있는 방법은 무엇입니까?

몇 가지 방법이 있습니다.

코드에이 배열이 있다고 상상해보십시오.

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) 또한 contact 방법은 두 배열을 병합하기위한 것이지만 배열 중 하나를 지정할 수 있으며 기본적으로 새로운 접촉 배열의 값을 복사합니다.

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

6
let a = [1,2,3];

이제 다음 중 하나를 수행하여 배열의 사본을 만들 수 있습니다.

let b = Array.from(a); 

또는

let b = [...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]입니다.

그러나 Array.from 위의 모든 방법에서 더 좋고 주로 배열을 복사 한다고 생각 합니다.


1
어느 것이 가장 빠릅니까?
Marc Frame


4

내 특별한 경우에는 배열이 그대로 유지되어 이것이 효과가 있는지 확인해야했습니다.

// 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]);
}

2
똑같은 일을하지만 덜 명백한 방식으로 함수를 호출하여 코드에 모호함을 추가하지 않은 +1. 슬라이스는 후드 아래에서 더 효율적 일 수 있지만 코드 작업을하는 사람에게는 이것이 의도를 보여줍니다. 또한 복사 할 내용을 필터링하려는 경우 나중에 최적화하기가 더 쉽습니다. 그러나 이것은 딥 카피를 처리하지 않으며 동일한 내부 객체가 참조로 새 배열에 전달됩니다. 이것은 당신이하고 싶은 것일 수도 있고 아닐 수도 있습니다.
동기화되지 않은

4

다차원 배열 / 객체를 복사합니다 :

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에게 감사드립니다.

출처 : 여기


3

댄, 멋진 트릭을 사용할 필요가 없습니다. 이 작업을 수행하여 arr1을 복사하기 만하면됩니다.

var arr2 = new Array(arr1);

지금 arr1arr2별도의 스택에 저장된 두 개의 다른 배열 변수이다. jsfiddle에서 이것을 확인하십시오 .


이것은 배열을 복사하지 않습니다. 원본 (즉 var arr2 = [arr1];) 을 참조하는 하나의 요소로 배열을 만듭니다 .
Timothy003

3

대입 연산자 ( =)를 사용하여 배열을 복사하려는 경우 복사본을 만들지 않고 포인터 / 참조를 배열에 복사하기 만합니다. 예를 들면 다음과 같습니다.

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

따라서 배열 내부에 복사하려는 객체 또는 배열이있는 경우 이러한 방법을 사용하지 마십시오. 즉, 이러한 방법은 기본 배열에만 사용하십시오.

다음 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))딥 클론이 필요하지 않고 성능에 문제가있는 경우 (많은 오버 헤드)를 사용하지 마십시오 .

소스 성능 테스트


3

배열에 int, char 또는 string 등과 같은 기본 데이터 유형의 요소가 포함되어 있으면 .slice () 또는 .map () 또는 spread operator ()와 같은 원래 배열의 사본을 리턴하는 메소드 중 하나를 사용할 수 있습니다 ES6 덕분에).

new_array = old_array.slice()

또는

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

또는

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

그러나 배열에 객체 (또는 배열) 또는 더 많은 중첩 된 객체 와 같은 복잡한 요소 가 포함되어 있으면 최상위에서 마지막 레벨까지 모든 요소의 사본을 작성해야합니다. 객체가 사용되므로 new_array에서 object_elements의 값을 변경하면 여전히 old_array에 영향을 미칩니다. old_array 의 DEEP COPY 를 만들 때 각 레벨에서이 복사 방법을 호출 할 수 있습니다 .

딥 카피의 경우 데이터 유형에 따라 각 레벨에서 위에서 언급 한 기본 데이터 유형에 대해 위의 방법을 사용하거나 많은 비용을 들이지 않고 딥 카피를 만들기 위해이 고가의 방법 (아래 언급 됨) 을 사용할 수 있습니다 .

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

요구 사항에 따라 사용할 수있는 다른 방법이 많이 있습니다. 배열을 다른 으로 복사하려고 할 때 어떤 일이 발생하는지에 대한 일반적인 아이디어를 제공하는 것에 대해서만 언급 했습니다 .


고마워요, 당신의 대답은 제 시나리오에서 일한 유일한 사람입니다
albert sh

2

객체 또는 배열의 새 복사본을 만들려면 객체의 속성이나 배열의 요소를 명시 적으로 복사해야합니다. 예를 들면 다음과 같습니다.

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

for (var i=0; i < arr1.length; i++) {
   arr2[i] = arr1[i];
}

변경 불가능한 기본 값 및 변경 가능한 오브젝트 참조에 대한 자세한 정보를 Google에서 검색 할 수 있습니다.


1
배열 객체의 속성을 명시 적으로 복사 할 필요는 없습니다. Chtiwi Malek의 답변을 참조하십시오.
Magne


2

ES6 스프레드 연산자를 사용하여 어레이를 복사 할 수도 있습니다.

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

2

복사하는 방법이 몇 가지 더 있습니다.

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



2

빠른 예 :

  1. 배열의 요소가 기본 유형 (문자열, 숫자 등) 인 경우

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' ]

  1. 배열의 요소가 객체 리터럴 인 경우 다른 배열 ({}, [])

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 ] ]

  1. 솔루션 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 ] ]


1

변형은 다음과 같습니다.

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
 */

eval 대신 JSON stringify / parse를 사용하는 것이 좋지만 또 다른 jsPerf 비교를 확인하는 것이 좋지만 참고 toSource는 표준이 아니며 Chrome에서 작동하지 않습니다.
dmi3y

1

새로 도입 된 Array.from것은 있지만 불행히도이 글을 쓰는 시점에서는 최신 Firefox 버전 (32 이상)에서만 지원됩니다. 다음과 같이 간단하게 사용할 수 있습니다.

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

참조 : 여기

또는 Array.prototype.mapidentity 함수와 함께 사용할 수 있습니다.

function identity(param)
{
    return param;
}

var arr1 = [1, 2, 3],
    clone = arr1.map(identity);

참조 : 여기


언급 +1 Array.from, Internet Explorer ( 소스 )를 제외한 모든 주요 브라우저에서 지원됩니다 . .
mgthomas99

주의 Array.from깊은 복사 / 깊은 복제를 제공하지 않는 데이터를 돌연변이됩니다.
Sudhansu Choudhary

1

객체를 포함하는 ES6 배열의 경우

cloneArray(arr) {
    return arr.map(x => ({ ...x }));
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.