development

Javascript에서 객체 배열을 어떻게 복제합니까?

big-blog 2020. 2. 25. 22:50
반응형

Javascript에서 객체 배열을 어떻게 복제합니까?


... 각 객체에는 동일한 배열 내의 다른 객체에 대한 참조가 있습니까?

내가 처음이 문제를 생각해 냈을 때

var clonedNodesArray = nodesArray.clone()

존재하고 자바 스크립트에서 객체를 복제하는 방법에 대한 정보를 검색했습니다. 나는 StackOverflow에 대한 질문찾았으며 (매우 동일한 @JohnResig에 의해 답변 됨) jQuery를 사용하면 할 수 있다고 지적했습니다.

var clonedNodesArray = jQuery.extend({}, nodesArray);

객체를 복제합니다. 나는 이것을 시도했지만, 이것은 배열에있는 객체의 참조 만 복사합니다. 만약 내가

nodesArray[0].value = "red"
clonedNodesArray[0].value = "green"

nodesArray [0] 및 clonedNodesArray [0]의 값이 모두 "green"으로 나타납니다. 그런 다음 시도했습니다

var clonedNodesArray = jQuery.extend(true, {}, nodesArray);

깊게 객체를 복사하지만 Firebug와 Opera Dragonfly에서 각각 " 너무 많은 재귀 "와 " 제어 스택 오버플로 "메시지를 받았습니다.

어떻게 하시겠습니까? 이조 차도해서는 안되는 일입니까? Javascript에서 재사용 가능한 방법이 있습니까?


얕은 복사본의 문제는 모든 개체가 복제되지 않는다는 것입니다. 각 객체에 대한 참조는 각 배열에서 고유하지만 궁극적으로 객체를 가져 오면 이전과 동일한 객체를 처리하게됩니다. 복제 방법에는 아무런 문제가 없습니다. Array.slice ()를 사용하여 동일한 결과가 발생합니다.

딥 카피에 문제가있는 이유는 순환 객체 참조로 끝나기 때문입니다. 딥은 가능한 한 깊게 진행되며 원이 있으면 브라우저가 사라질 때까지 계속 무한대로 진행됩니다.

데이터 구조를 유향 비순환 그래프로 표현할 수없는 경우, 심층 복제를위한 다목적 방법을 찾을 수 있을지 모르겠습니다. 순환 그래프는 많은 까다로운 코너 사례를 제공하며 일반적인 작업이 아니기 때문에 모든 사람이 완전한 솔루션을 작성했다고 의심합니다. 이 페이지에서이 문제에 대한 좋은 의견을 찾았습니다 .

순환 참조가있는 객체 배열의 깊은 사본이 필요한 경우 특수 데이터 구조를 처리하기 위해 고유 한 메소드를 코딩해야하므로 다중 패스 복제본이 필요합니다.

  1. 라운드 1에서는 배열의 다른 객체를 참조하지 않는 모든 객체를 복제합니다. 각 객체의 원점을 추적합니다.
  2. 두 번째 라운드에서 객체를 서로 연결하십시오.

객체에 JSON 직렬화 가능 컨텐츠 (함수, 아니오 Number.POSITIVE_INFINITY등)가 포함되어 있으면 배열이나 객체를 복제하기 위해 루프가 필요하지 않습니다. 다음은 순수한 바닐라 단선 솔루션입니다.

var clonedArray = JSON.parse(JSON.stringify(nodesArray))

아래 설명을 요약하면이 방법의 주요 장점은 배열 자체뿐만 아니라 배열의 내용도 복제한다는 것입니다. 주요 단점은 JSON 직렬화 가능 콘텐츠에 대한 작업의 한계이며 성능입니다 ( slice기반 접근 방식 보다 훨씬 나쁩니다 ).


Object.assign을 사용 하여 객체 배열 복제를 해결했습니다.

const newArray = myArray.map(a => Object.assign({}, a));

스프레드 구문으로 더 짧거나

const newArray = myArray.map(a => ({...a}));

얕은 사본 만 있으면 정말 쉬운 방법은 다음과 같습니다.

new_array = old_array.slice(0);

이 복제를 수행하는 가장 좋은 최신 방법은 다음과 같습니다.

"..."ES6 스프레드 연산자를 사용합니다.

가장 간단한 예는 다음과 같습니다.

var clonedObjArray = [...oldObjArray];

이런 식으로 배열을 개별 값으로 분산시키고 [] 연산자를 사용하여 새 배열에 넣습니다.

작동 방식을 보여주는 더 긴 예는 다음과 같습니다.

let objArray = [ {a:1} , {b:2} ];

let refArray = objArray; // this will just point to the objArray
let clonedArray = [...objArray]; // will clone the array

console.log( "before:" );
console.log( "obj array" , objArray );
console.log( "ref array" , refArray );
console.log( "cloned array" , clonedArray );

objArray[0] = {c:3};

console.log( "after:" );
console.log( "obj array" , objArray ); // [ {c:3} , {b:2} ]
console.log( "ref array" , refArray ); // [ {c:3} , {b:2} ]
console.log( "cloned array" , clonedArray ); // [ {a:1} , {b:2} ]


이것은 나를 위해 작동합니다 :

var clonedArray = $.map(originalArray, function (obj) {
                      return $.extend({}, obj);
                  });

그리고 배열에 객체의 깊은 사본이 필요한 경우 :

var clonedArray = $.map(originalArray, function (obj) {
                      return $.extend(true, {}, obj);
                  });

$.evalJSON($.toJSON(origArray));

은 이전 배열을 참조하지 않고 이전 배열에서 새 배열을 작성하고 맵 내에서 새 오브젝트를 작성하고 특성 (키)을 반복하고 이전 배열 오브젝트의 값을 코어 응답 특성을 새 오브젝트에 지정합니다.

이것은 정확히 동일한 객체 배열을 만듭니다.

let newArray = oldArray.map(a => {
               let newObject = {};
               Object.keys(a).forEach(propertyKey => {
                    newObject[propertyKey] = a[propertyKey];
               });
               return newObject ;
});

고통스러운 재귀를하지 않고 문제의 대상에 대한 모든 세부 사항을 알지 않고도이 작업을 수행하는 간단한 방법이있을 수 있습니다. jQuery를 사용하여 jQuery를 사용하여 객체를 JSON으로 변환 $.toJSON(myObjectArray)한 다음 JSON 문자열을 가져와 객체로 다시 평가하십시오. 밤! 완료했습니다! 문제 해결됨. :)

var oldObjArray = [{ Something: 'blah', Cool: true }];
var newObjArray = eval($.toJSON(oldObjArray));

"Javascript에서 객체 배열 복제"문제에 대한 간단하고 명시적인 해결책이 없기 때문에이 질문에 대답하고 있습니다.

function deepCopy (arr) {
    var out = [];
    for (var i = 0, len = arr.length; i < len; i++) {
        var item = arr[i];
        var obj = {};
        for (var k in item) {
            obj[k] = item[k];
        }
        out.push(obj);
    }
    return out;
}

// test case

var original = [
    {'a' : 1},
    {'b' : 2}
    ];

var copy = deepCopy(original);

// change value in copy
copy[0]['a'] = 'not 1';

// original[0]['a'] still equals 1

이 솔루션은 배열 값을 반복 한 다음 객체 키를 반복하여 후자를 새 객체에 저장 한 다음 새 객체를 새 배열로 푸시합니다.

jsfiddle을 참조하십시오 . 참고 : 배열 내의 객체 에는 단순 .slice()하거나 [].concat()충분하지 않습니다 .


JQuery extend가 제대로 작동하면 객체가 아닌 배열을 복제하도록 지정해야합니다 ( extend 메소드의 매개 변수로 {} 대신 [] 참고 ).

var clonedNodesArray = jQuery.extend([], nodesArray);

Daniel Lew가 언급했듯이 순환 그래프에는 몇 가지 문제가 있습니다. 이 문제가 발생 clone()하면 문제가있는 객체에 특수한 방법을 추가 하거나 이미 복사 한 객체를 기억합니다.

copyCount코드에서 복사 할 때마다 1 씩 증가 하는 변수 로 처리합니다. copyCount현재 복사 프로세스보다 낮은 개체 가 복사됩니다. 그렇지 않은 경우 이미 존재하는 사본을 참조해야합니다. 원본에서 사본으로 연결해야합니다.

여전히 문제가 하나 있습니다 : 메모리. 한 객체에서 다른 객체로이 참조가있는 경우 브라우저는 해당 객체가 항상 어딘가에서 참조되기 때문에 해당 객체를 해제 할 수 없습니다. 모든 복사 참조를 Null로 설정하는 두 번째 단계를 거쳐야합니다. (이 작업을 수행 하면 두 번째 패스에서 값을 재설정 할 수 있으므로 copyCount부울 isCopied이 충분 하지 않아도됩니다 .)


이 방법은 매우 간단하며 원래 배열을 수정하지 않고 복제본을 수정할 수 있습니다.

// Original Array
let array = [{name: 'Rafael'}, {name: 'Matheus'}];

// Cloning Array
let clone = array.map(a => {return {...a}})

// Editing the cloned array
clone[1].name = 'Carlos';


console.log('array', array)
// [{name: 'Rafael'}, {name: 'Matheus'}]

console.log('clone', clone)
// [{name: 'Rafael'}, {name: 'Carlos'}]


lodash는 cloneDeep다음과 같은 기능을 수행합니다.

var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);

Array.slice는 .. 배열의 배열 또는 일부를 복사하는 데 사용할 수 있습니다 http://www.devguru.com/Technologies/Ecmascript/Quickref/Slice.html ..이 문자열과 숫자로 작동합니다 -에서 문자열을 변경 한 배열은 다른 배열에 영향을 미치지 않지만 개체는 여전히 참조로 복사되므로 한 배열에서 참조 된 개체를 변경하면 다른 배열에 영향을 미칩니다.

여기에 유용한 JavaScript 실행 취소 관리자의 예가 있습니다. http://www.ridgway.co.za/archive/2007/11/07/simple-javascript-undo-manager-for-dtos.aspx


내 접근 방식 :

var temp = { arr : originalArray };
var obj = $.extend(true, {}, temp);
return obj.arr;

원래 배열을 멋지고 깨끗하고 깊게 복제합니다. 원래로 다시 참조되는 객체는 없습니다 :-)


eval () (JS의 가장 많이 사용되는 기능이며 코드를 느리게 만듭니다) 및 slice (0) (간단한 데이터 유형에만 작동)

이것은 나에게 가장 좋은 해결책입니다.

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

나는이 문제에 꽤 좌절했다. 분명히 일반 배열을 $ .extend 메서드로 보낼 때 문제가 발생합니다. 그래서 수정하기 위해 약간의 검사를 추가했으며 일반 배열, jQuery 배열 및 모든 객체와 완벽하게 작동합니다.

jQuery.extend({
    deepclone: function(objThing) {
        // return jQuery.extend(true, {}, objThing);
        /// Fix for arrays, without this, arrays passed in are returned as OBJECTS! WTF?!?!
        if ( jQuery.isArray(objThing) ) {
            return jQuery.makeArray( jQuery.deepclone($(objThing)) );
        }
        return jQuery.extend(true, {}, objThing);
    },
});

다음을 사용하여 호출하십시오.

var arrNewArrayClone = jQuery.deepclone(arrOriginalArray);
// Or more simply/commonly
var arrNewArrayClone = $.deepclone(arrOriginalArray);

이것은 배열, 객체, null 및 기타 스칼라 값을 깊게 복사하고 네이티브가 아닌 함수의 속성을 깊게 복사합니다 (이는 일반적이지 않지만 가능합니다). 효율성을 위해 배열에서 숫자가 아닌 속성을 복사하지 않습니다.

function deepClone (item) {
  if (Array.isArray(item)) {
    var newArr = [];
    for (var i = item.length; i-- > 0;) {
      newArr[i] = deepClone(item[i]);
    }
    return newArr;
  }
  if (typeof item === 'function' && !(/\(\) \{ \[native/).test(item.toString())) {
    var obj;
    eval('obj = '+ item.toString());
    for (var k in item) {
      obj[k] = deepClone(item[k]);
    }
    return obj;
  }
  if (item && typeof item === 'object') {
    var obj = {};
    for (var k in item) {
      obj[k] = deepClone(item[k]);
    }
    return obj;
  }
  return item;
}

새로운 ECMAScript 6 Object.assign 메소드를 사용합니다 .

let oldObject = [1,3,5,"test"];
let newObject = Object.assign({}, oldObject);

이 메소드의 첫 번째 인수는 업데이트 할 배열입니다. 새 객체를 원하기 때문에 빈 객체를 전달합니다.

이 구문을 사용할 수도 있습니다.이 구문은 동일하지만 짧습니다.

let newObject = [...oldObject];

다차원 배열을 복제하는 간단한 재귀 배열 방법을 발명 할 수 있습니다. 중첩 배열 내의 객체는 소스 배열의 해당 객체에 대한 참조를 유지하지만 배열은 그렇지 않습니다.

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


jQuery로 :

var target= [];
$.each(source, function() {target.push( $.extend({},this));});

다음 코드는 객체와 배열딥 카피를 재귀 적으로 수행 합니다 .

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

출처


자바 스크립트에서 딥 클로닝을위한 몇 가지 우아한 방법

https://mootools.net/core/docs/1.6.0/Types/Object

https://scotch.io/bar-talk/copying-objects-in-javascript

1) 객체 복제를위한 바닐라 자바 ​​스크립트 방법

2) 객체를 딥 클론하기위한 JSON 라이브러리의 영리한 활용

3) jQuery의 $ .extend () 함수 사용

4) Mootools의 clone () 함수를 사용하여 객체 복제


필자 Object.create는 모든 최신 브라우저에서 지원되는 JavaScript 구조를 주로 사용하는 일반적인 복제 방법을 작성했다고 생각 합니다. 코드는 다음과 같습니다

function deepClone (item) {
  if (Array.isArray(item)) {
    var newArr = [];

    for (var i = item.length; i-- !== 0;) {
      newArr[i] = deepClone(item[i]);
    }

    return newArr;
  }
  else if (typeof item === 'function') {
    eval('var temp = '+ item.toString());
    return temp;
  }
  else if (typeof item === 'object')
    return Object.create(item);
  else
    return item;
}

객체를 복제하기 위해 ECMAScript 6을 제안하려고했습니다 reduce().

const newArray=myArray.reduce((array, element)=>array.push(Object.assign({}, element)), []);

그러나 솔직히 @dinodsaurus의 답변이 더 좋습니다. 나는이 버전을 다른 옵션으로 여기에 넣고 있지만 개인적 map()으로 @dinodsaurus가 제안한대로 사용할 것입니다.


Underscore 또는 Babel이 있는지 여부에 따라 어레이를 딥 복제하는 다른 방법의 벤치 마크가 있습니다.

https://jsperf.com/object-rest-spread-vs-clone/2

babel이 가장 빠른 것 같습니다.

var x = babel({}, obj)

       var game_popularity = [
            { game: "fruit ninja", popularity: 78 },
            { game: "road runner", popularity: 20 },
            { game: "maze runner", popularity: 40 },
            { game: "ludo", popularity: 75 },
            { game: "temple runner", popularity: 86 }
        ];
        console.log("sorted original array before clonning");
        game_popularity.sort((a, b) => a.popularity < b.popularity);
        console.log(game_popularity);


        console.log("clone using object assign");
        const cl2 = game_popularity.map(a => Object.assign({}, a));
        cl2[1].game = "clash of titan";
        cl2.push({ game: "logan", popularity: 57 });
        console.log(cl2);


        //adding new array element doesnt reflect in original array
        console.log("clone using concat");
        var ph = []
        var cl = ph.concat(game_popularity);

        //copied by reference ?
        cl[0].game = "rise of civilization";

        game_popularity[0].game = 'ping me';
        cl.push({ game: "angry bird", popularity: 67 });
        console.log(cl);

        console.log("clone using ellipses");
        var cl3 = [...game_popularity];
        cl3.push({ game: "blue whale", popularity: 67 });
        cl3[2].game = "harry potter";
        console.log(cl3);

        console.log("clone using json.parse");
        var cl4 = JSON.parse(JSON.stringify(game_popularity));
        cl4.push({ game: "home alone", popularity: 87 });
        cl4[3].game ="lockhead martin";
        console.log(cl4);

        console.log("clone using Object.create");
        var cl5 = Array.from(Object.create(game_popularity));
        cl5.push({ game: "fish ville", popularity: 87 });
        cl5[3].game ="veto power";
        console.log(cl5);


        //array function
        console.log("sorted original array after clonning");
        game_popularity.sort((a, b) => a.popularity < b.popularity);
        console.log(game_popularity);


        console.log("Object.assign deep clone object array");
        console.log("json.parse deep clone object array");
        console.log("concat does not deep clone object array");
        console.log("ellipses does not deep clone object array");
        console.log("Object.create does not deep clone object array");


        Output:


        sorted original array before clonning
        [ { game: 'temple runner', popularity: 86 },
        { game: 'fruit ninja', popularity: 78 },
        { game: 'ludo', popularity: 75 },
        { game: 'maze runner', popularity: 40 },
        { game: 'road runner', popularity: 20 } ]
        clone using object assign
        [ { game: 'temple runner', popularity: 86 },
        { game: 'clash of titan', popularity: 78 },
        { game: 'ludo', popularity: 75 },
        { game: 'maze runner', popularity: 40 },
        { game: 'road runner', popularity: 20 },
        { game: 'logan', popularity: 57 } ]
        clone using concat
        [ { game: 'ping me', popularity: 86 },
        { game: 'fruit ninja', popularity: 78 },
        { game: 'ludo', popularity: 75 },
        { game: 'maze runner', popularity: 40 },
        { game: 'road runner', popularity: 20 },
        { game: 'angry bird', popularity: 67 } ]
        clone using ellipses
        [ { game: 'ping me', popularity: 86 },
        { game: 'fruit ninja', popularity: 78 },
        { game: 'harry potter', popularity: 75 },
        { game: 'maze runner', popularity: 40 },
        { game: 'road runner', popularity: 20 },
        { game: 'blue whale', popularity: 67 } ]
        clone using json.parse
        [ { game: 'ping me', popularity: 86 },
        { game: 'fruit ninja', popularity: 78 },
        { game: 'harry potter', popularity: 75 },
        { game: 'lockhead martin', popularity: 40 },
        { game: 'road runner', popularity: 20 },
        { game: 'home alone', popularity: 87 } ]
        clone using Object.create
        [ { game: 'ping me', popularity: 86 },
        { game: 'fruit ninja', popularity: 78 },
        { game: 'harry potter', popularity: 75 },
        { game: 'veto power', popularity: 40 },
        { game: 'road runner', popularity: 20 },
        { game: 'fish ville', popularity: 87 } ]
        sorted original array after clonning
        [ { game: 'ping me', popularity: 86 },
        { game: 'fruit ninja', popularity: 78 },
        { game: 'harry potter', popularity: 75 },
        { game: 'veto power', popularity: 40 },
        { game: 'road runner', popularity: 20 } ]

        Object.assign deep clone object array
        json.parse deep clone object array
        concat does not deep clone object array
        ellipses does not deep clone object array
        Object.create does not deep clone object array

JavaScript에서 배열 및 객체 복사는 원점 값을 변경하므로 깊은 복사가 이에 대한 솔루션입니다.

딥 카피는 실제로 새로운 배열을 생성하고 값을 복사하는 것을 의미합니다.

JSON.parseJSON.stringify깊은 복사에 가장 간단한 방법입니다. JSON.stringify()메소드는 JavaScript 값을 JSON 문자열로 변환합니다. 메소드는 JSON.parse()JSON 문자열을 구문 분석하여 문자열에 설명 된 JavaScript 값 또는 오브젝트를 구성합니다.

// 딥 클론

let a = [{ x:{z:1} , y: 2}];
let b = JSON.parse(JSON.stringify(a));
b[0].x.z=0

console.log(JSON.stringify(a)); //[{"x":{"z":1},"y":2}]
console.log(JSON.stringify(b)); // [{"x":{"z":0},"y":2}]

자세한 내용은 여기를 읽으십시오


여기 내 솔루션이 있습니다. 객체 배열이나 맵에서 작동합니다. 이 솔루션은 방법을 유지합니다.

딥 카피는 실제로 새로운 배열을 생성하고 값을 복사하는 것을 의미합니다.

이것은 나에게 가장 좋은 해결책입니다.

deepCopy(inputObj: any) {
    var newObj = inputObj;
    if (inputObj && typeof inputObj === "object") {
        newObj = Object.prototype.toString.call(inputObj) === "[object Array]" ? [] : {};
        for (var i in inputObj) {
            newObj[i] = this.deepCopy(inputObj[i]);
        }

        //For maps
        if(Object.prototype.toString.call(inputObj) === "[object Map]"){
            newObj = new Map;
            inputObj.forEach((v,k) =>{
                newObj.set(k,this.deepCopy(v));
            });
        }
    }
    return newObj;
}

참고 URL : https://stackoverflow.com/questions/597588/how-do-you-clone-an-array-of-objects-in-javascript



반응형