자바 스크립트에서 딥 클론하는 방법


105

Javascript 객체를 어떻게 심층 복제합니까?

내가 좋아하는 프레임 워크를 기반으로 다양한 기능이 알고 JSON.parse(JSON.stringify(o))하고 $.extend(true, {}, o)있지만, 그와 같은 프레임 워크를 사용하지 않습니다.

딥 클론을 생성하는 가장 우아하고 효율적인 방법은 무엇입니까?

우리는 어레이 복제와 같은 엣지 케이스에 관심이 있습니다. 프로토 타입 체인을 깨지 않고 자체 참조를 처리합니다.

우리는 DOM 객체의 복사를 지원하는 데 관심이 없습니다 .cloneNode. 그 이유 때문에 존재하기 때문입니다.

주로 node.jsV8 엔진의 ES5 기능 을 사용할 때 딥 클론을 사용하고 싶기 때문에 허용됩니다.

[편집하다]

누군가 제안하기 전에 객체에서 프로토 타입으로 상속하여 복제본을 생성하는 것과 복제 하는 것 사이에는 뚜렷한 차이점이 있습니다. 전자는 프로토 타입 체인을 엉망으로 만듭니다.

[추가 편집]

귀하의 답변을 읽은 후 전체 개체를 복제하는 것이 매우 위험하고 어려운 게임이라는 성가신 발견에 도달했습니다. 예를 들어 다음 클로저 기반 객체를 살펴보십시오.

var o = (function() {
     var magic = 42;

     var magicContainer = function() {
          this.get = function() { return magic; };
          this.set = function(i) { magic = i; };
     }

      return new magicContainer;
}());

var n = clone(o); // how to implement clone to support closures

객체를 복제하고 복제 시점에 동일한 상태를 가지지 만 oJS에서 JS 파서를 작성하지 않고 는 상태를 변경할 수없는 복제 함수를 작성하는 방법이 있습니까?

더 이상 그러한 기능이 실제로 필요하지 않아야합니다. 이것은 단순한 학문적 관심사입니다.


2
중복으로 표시되기 전에 stackoverflow.com/questions/122102/…를 살펴 보았고 모든 엣지 케이스를 다룬 답을 찾지 못했습니다.
Raynos 2010

"추가 편집"섹션의 요구 사항은 개체 자체의 "도움"없이는 달성 할 수 없습니다. 이러한 개인 변수는 진정으로 개인 변수 이고 결과적으로 일반 clone함수에서 액세스 할 수 없기 때문 입니다. 해당 개체는 자체 맞춤형 clone방법을 노출해야합니다 .
trincot

나는 오늘 밤 이것에 대해 많은 것을 읽었고, 내가 찾은 리소스 중에는 브라우저에서 구조화 된 복제 알고리즘에 액세스하기위한 몇 가지 해킹이 포함 된 추악한 블로그 게시물이 있습니다. dassur.ma/things/deep-copy
고양이

답변:


67

복제하려는 항목에 따라 다릅니다. 이것은 진정한 JSON 객체입니까 아니면 JavaScript의 객체입니까? 복제를 수행하려면 문제가 발생할 수 있습니다. 어떤 문제? 아래에서 설명하겠습니다. 먼저 객체 리터럴, 프리미티브, 배열 및 DOM 노드를 복제하는 코드 예제입니다.

function clone(item) {
    if (!item) { return item; } // null, undefined values check

    var types = [ Number, String, Boolean ], 
        result;

    // normalizing primitives if someone did new String('aaa'), or new Number('444');
    types.forEach(function(type) {
        if (item instanceof type) {
            result = type( item );
        }
    });

    if (typeof result == "undefined") {
        if (Object.prototype.toString.call( item ) === "[object Array]") {
            result = [];
            item.forEach(function(child, index, array) { 
                result[index] = clone( child );
            });
        } else if (typeof item == "object") {
            // testing that this is DOM
            if (item.nodeType && typeof item.cloneNode == "function") {
                result = item.cloneNode( true );    
            } else if (!item.prototype) { // check that this is a literal
                if (item instanceof Date) {
                    result = new Date(item);
                } else {
                    // it is an object literal
                    result = {};
                    for (var i in item) {
                        result[i] = clone( item[i] );
                    }
                }
            } else {
                // depending what you would like here,
                // just keep the reference, or create new object
                if (false && item.constructor) {
                    // would not advice to do that, reason? Read below
                    result = new item.constructor();
                } else {
                    result = item;
                }
            }
        } else {
            result = item;
        }
    }

    return result;
}

var copy = clone({
    one : {
        'one-one' : new String("hello"),
        'one-two' : [
            "one", "two", true, "four"
        ]
    },
    two : document.createElement("div"),
    three : [
        {
            name : "three-one",
            number : new Number("100"),
            obj : new function() {
                this.name = "Object test";
            }   
        }
    ]
})

이제 REAL 개체 복제를 시작할 때 발생할 수있는 문제에 대해 이야기하겠습니다. 나는 지금 당신이 다음과 같은 것을함으로써 창조하는 물체에 대해 이야기하고 있습니다.

var User = function(){}
var newuser = new User();

물론 그것들을 복제 할 수 있습니다. 그것은 문제가되지 않습니다. 모든 객체는 생성자 속성을 노출하고 객체를 복제하는 데 사용할 수 있지만 항상 작동하지는 않습니다. for in이 개체에 대해 간단하게 할 수도 있지만 같은 방향으로 진행됩니다-문제. 또한 코드 내에 복제 기능을 포함 시켰지만 if( false )문에 의해 제외되었습니다 .

그렇다면 복제가 왜 고통 스러울 수 있습니까? 우선 모든 객체 / 인스턴스에는 상태가있을 수 있습니다. 예를 들어 객체에 개인 변수가 없는지 확신 할 수 없으며,이 경우 객체를 복제하면 상태가 깨집니다.

상태가 없다고 상상해보세요. 괜찮습니다. 그렇다면 여전히 또 다른 문제가 있습니다. "생성자"방법을 통한 복제는 또 다른 장애물을 제공합니다. 인수 종속성입니다. 이 개체를 만든 사람이 어떤 종류의

new User({
   bike : someBikeInstance
});

이 경우 운이 좋지 않은 경우 someBikeInstance가 일부 컨텍스트에서 생성되었을 수 있으며 해당 컨텍스트는 복제 메서드에 대해 알 수 없습니다.

그래서 뭘 할건데? 당신은 여전히 for in솔루션을 할 수 있고 그러한 객체를 일반 객체 리터럴처럼 취급 할 수 있지만, 그러한 객체를 전혀 복제하지 않고 단지이 객체의 참조를 전달하는 것이 아이디어일까요?

또 다른 해결책은 복제해야하는 모든 객체가이 부분을 스스로 구현하고 적절한 API 메서드 (예 : cloneObject)를 제공해야한다는 규칙을 설정할 수 있다는 것입니다. 무엇 뭔가 cloneNodeDOM에 대해하고있다.

당신이 결정합니다.


나는 상태를 숨기기 위해 클로저를 사용하는 객체를 다루는 장애물에 직면했습니다. 개체와 전체 상태를 복제하지만 복제가 원본 상태를 자체적으로 변경할 수 없다는 것을 어떻게 보장 할 수 있습니까? result = new item.constructor();나쁜 점 은 생성자 함수와 항목 객체가 주어지면 생성자에 전달 된 모든 매개 변수를 RE 할 수 있어야한다는 것입니다.
Raynos 2010

7
@Raynos : 객체가 클로저를 사용하여 상태를 숨기면 복제 할 수 없습니다. 따라서 용어 '폐쇄'. nemisj가 마지막에 말했듯이 가장 좋은 방법은 복제 (또는 직렬화 / 역 직렬화)를위한 API 메서드를 구현하는 것입니다.
Michiel Kalkman 2010

@MichielKalkman 나는 그런 느낌이 들었다. 누군가가 이것에 대한 정말 영리한 해결책을 가지고 있었을지라도.
Raynos 2010

효과는 false && item.constructor무엇입니까? 하지 즉 if쓸모?
Gabriel Petrovay 2014-08-12

2
@GabrielPetrovay if절대 실행되지 않기 때문에 기능적 관점에서 '쓸모'가 없지만, 사용하려고 시도 할 수있는 가상의 구현을 보여주는 학문적 목적을 가지고 있습니다. 저자는 나중에 설명하는 이유 때문에 조언하지 않습니다. 따라서 예, else조건이 평가 될 때마다 절 을 트리거 하지만 코드가 거기에 있어야하는 이유가 있습니다.
Gui Imamura

155

아주 간단한 방법, 어쩌면 너무 간단 할 수도 있습니다.

var cloned = JSON.parse(JSON.stringify(objectToClone));

12
객체 값이 함수가 아닌 한 훌륭합니다. 그 시점에서 받아 들여진 대답과 같은 것을 사용해야합니다. 또는 cloneDeepLodash 와 같은 도우미 기능을 사용하십시오 .
matthoiland

31
객체 값이 함수이면 객체는 JSON이 아닙니다.
Jos de Jong

5
함수를 사용하는 것보다 복제하는 것이 정당한 사용 사례는 무엇입니까?
G. Ghez 2016 년

3
내가 올바르게 기억한다면 이것은 또한 날짜를 문자열로 변환합니다.
Peter

3
@ G.Ghez 당신은 기능이 손실 될 수있는 기능을 포함하는 객체 .. 복제 경우
피터

38

JSON.parse(JSON.stringify())딥 카피 자바 스크립트 객체 의 조합은 JSON 데이터를위한 것이기 때문에 비효율적 인 해킹입니다. undefined또는의 값을 지원하지 않으며 Javascript 객체를 JSON으로 "문자열 화"(마샬링) 할 때 function () {}단순히 무시합니다 null.

더 나은 해결책은 전체 복사 기능을 사용하는 것입니다. 아래 함수는 객체를 딥 복사하며 타사 라이브러리 (jQuery, LoDash 등)가 필요하지 않습니다.

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

7
aObject (또는 포함 된 다른 객체)에 자체 참조가 포함 된 경우는 예외입니다. stackoverflow ™!
e2-e4

@ ringø- "자체 참조"테스트 케이스를 제공 할 수 있습니까?
tfmontague

4
var o = { a:1, b:2 } ; o["oo"] = { c:3, m:o };
e2-e4

4
이 솔루션이 마음에 듭니다. 나를 위해 만 수정이 null 값을 처리했다 : bObject[k] = (v === null) ? null : (typeof v === "object") ? copy(v) : v;
데이비드 커크 랜드

2
이 기능은 간단하고 이해하기 쉬우 며 거의 모든 경우를 포착합니다. 당신이 얻을 수있는 가장 완벽한 자바 스크립트 세계에서.
icc97

21

다음은 순환 참조가있는 객체에서도 작동하는 ES6 함수입니다.

function deepClone(obj, hash = new WeakMap()) {
    if (Object(obj) !== obj) return obj; // primitives
    if (hash.has(obj)) return hash.get(obj); // cyclic reference
    const result = obj instanceof Set ? new Set(obj) // See note about this!
                 : obj instanceof Map ? new Map(Array.from(obj, ([key, val]) => 
                                        [key, deepClone(val, hash)])) 
                 : obj instanceof Date ? new Date(obj)
                 : obj instanceof RegExp ? new RegExp(obj.source, obj.flags)
                 // ... add here any specific treatment for other classes ...
                 // and finally a catch-all:
                 : obj.constructor ? new obj.constructor() 
                 : Object.create(null);
    hash.set(obj, result);
    return Object.assign(result, ...Object.keys(obj).map(
        key => ({ [key]: deepClone(obj[key], hash) }) ));
}

// Sample data
var p = {
  data: 1,
  children: [{
    data: 2,
    parent: null
  }]
};
p.children[0].parent = p;

var q = deepClone(p);

console.log(q.children[0].parent.data); // 1

세트 및 맵에 대한 참고 사항

세트 및 맵의 키를 다루는 방법은 논쟁의 여지가 있습니다. 이러한 키는 종종 원시적이지만 (이 경우 논쟁이 없음) 객체가 수도 있습니다. 이 경우 질문은 다음과 같습니다. 해당 키를 복제해야합니까?

이 작업을 수행해야한다고 주장 할 수 있으므로 해당 개체가 복사본에서 변경되면 원본의 개체는 영향을받지 않으며 그 반대의 경우도 마찬가지입니다.

반면에 has키를 설정 / 매핑 하는 경우 원본과 사본 모두에서 이것이 사실이어야합니다. 적어도 둘 중 하나에 변경이 이루어지기 전에 말입니다 . 복사본이 이전에 발생하지 않은 키가있는 Set / Map이면 이상 할 수 있습니다 (복제 프로세스 중에 생성 되었기 때문에). 이는 주어진 객체가 어떤 코드인지 알아야하는 코드에는별로 유용하지 않습니다. Set / Map을 입력하거나 입력하지 않습니다.

아시다시피 저는 두 번째 의견에 가깝습니다. 세트 및 맵의 키 는 동일하게 유지되어야하는 ( 참고 문헌 일 수 있음 )입니다.

이러한 선택은 종종 다른 (사용자 정의) 개체와 함께 표시됩니다. 복제 된 개체가 특정 경우에 어떻게 작동할지에 따라 달라지는 일반적인 해결책은 없습니다.


날짜와 정규 표현식 처리하지 않습니다
mkeremguc

1
@mkeremguc, 귀하의 의견에 감사드립니다. 날짜와 정규 표현식을 지원하도록 코드를 업데이트했습니다.
trincot 2017.11.09

지도를 포함한 멋진 솔루션. es6 세트 복제에 대한 지원이 누락되었습니다.
Robert Biggs

내가 착각하지 않았다면 다음과 같이 세트에 대한 지원을 추가 할 수 있습니다.if (object instanceof Set) Array.from(object, val => result.add(deepClone(val, hash)));
Robert Biggs

@RobertBiggs, 그게 하나의 가능성이지만, 제 생각에는 Set에 특정 키가 있으면 해당 Set의 복제 된 버전에서도 마찬가지입니다. 제안 된 코드를 사용하면 키가 객체이면 사실이 아닙니다. 따라서 키를 복제 하지 않는 것이 좋습니다 . 그러면 실제로 예상대로 작동 할 것이라고 생각합니다. 그 점에서 내 대답의 업데이트를 참조하십시오.
trincot

10

Underscore.js있는 contrib 라이브러리 라이브러리라는 함수가 스냅 샷을 깊은 클론 오브젝트 그

소스의 스 니펫 :

snapshot: function(obj) {
  if(obj == null || typeof(obj) != 'object') {
    return obj;
  }

  var temp = new obj.constructor();

  for(var key in obj) {
    if (obj.hasOwnProperty(key)) {
      temp[key] = _.snapshot(obj[key]);
    }
  }

  return temp;
}

라이브러리가 프로젝트에 연결되면 간단히 다음을 사용하여 함수를 호출합니다.

_.snapshot(object);

4
좋은 솔루션, 기억해야 할 점 : 클론과 원본은 동일한 프로토 타입을 공유합니다. 그것이 문제라면 "temp .__ proto__ = .snapshot (obj .__ proto_ );" 를 추가 할 수 있습니다 . "return temp"바로 위에 있고 'no enumerate'로 표시된 속성이있는 내장 클래스를 지원하려면 "for (obj의 var key)"대신 getOwnPropertyNames ()에서 반복 할 수 있습니다.
Ronen Ness

3

이것이 제가 사용하는 딥 클로닝 방법입니다. 좋습니다. 제안 해 주시기 바랍니다.

function deepClone (obj) {
    var _out = new obj.constructor;

    var getType = function (n) {
        return Object.prototype.toString.call(n).slice(8, -1);
    }

    for (var _key in obj) {
        if (obj.hasOwnProperty(_key)) {
            _out[_key] = getType(obj[_key]) === 'Object' || getType(obj[_key]) === 'Array' ? deepClone(obj[_key]) : obj[_key];
        }
    }
    return _out;
}

3

다른 사람들이 이것과 유사한 질문에 대해 언급했듯이, 일반적인 의미에서 "객체"를 복제하는 것은 JavaScript에서 모호합니다.

그러나 내가 "데이터"객체라고 부르는 객체 클래스가 있습니다. 즉, 단순히 { ... }리터럴 및 / 또는 간단한 속성 할당으로 생성되거나 복제하려는 것이 합당한 JSON에서 역 직렬화 된 객체 입니다. 오늘은 대규모 데이터 세트에 대해 어떤 일이 발생하는지 테스트하기 위해 서버에서 수신 한 데이터를 인위적으로 5 배 확장하고 싶었지만, 사물 (배열)과 그 자식은 사물이 제대로 작동하려면 별개의 개체 여야했습니다. 복제를 통해이 작업을 수행하여 데이터 세트를 늘릴 수있었습니다.

return dta.concat(clone(dta),clone(dta),clone(dta),clone(dta));

내가 종종 데이터 개체를 복제하는 다른 곳은 데이터를 보내기 전에 데이터 모델의 개체에서 상태 필드를 제거하려는 호스트에 데이터를 다시 제출하는 것입니다. 예를 들어, 복제 될 때 개체에서 "_"로 시작하는 모든 필드를 제거 할 수 있습니다.

다음은 복제 할 멤버를 선택하는 선택기와 지원 배열 (컨텍스트를 결정하기 위해 "경로"문자열을 사용함)을 포함하여 일반적으로이 작업을 수행하기 위해 작성한 코드입니다.

function clone(obj,sel) {
    return (obj ? _clone("",obj,sel) : obj);
    }

function _clone(pth,src,sel) {
    var ret=(src instanceof Array ? [] : {});

    for(var key in src) {
        if(!src.hasOwnProperty(key)) { continue; }

        var val=src[key], sub;

        if(sel) {
            sub+=pth+"/"+key;
            if(!sel(sub,key,val)) { continue; }
            }

        if(val && typeof(val)=='object') {
            if     (val instanceof Boolean) { val=Boolean(val);        }
            else if(val instanceof Number ) { val=Number (val);        }
            else if(val instanceof String ) { val=String (val);        }
            else                            { val=_clone(sub,val,sel); }
            }
        ret[key]=val;
        }
    return ret;
    }

Null이 아닌 루트 개체를 가정하고 멤버 선택이 없다고 가정하는 가장 간단하고 합리적인 딥 클론 솔루션은 다음과 같습니다.

function clone(src) {
    var ret=(src instanceof Array ? [] : {});
    for(var key in src) {
        if(!src.hasOwnProperty(key)) { continue; }
        var val=src[key];
        if(val && typeof(val)=='object') { val=clone(val);  }
        ret[key]=val;
        }
    return ret;
    }

2

이제 Underscore.js 의 상위 집합 인 Lo-Dash 에는 몇 가지 딥 클론 기능이 있습니다.

저자 자신 의 답변에서 :

lodash underscore 최신 안정 버전의 Underscore와의 호환성을 보장하기 위해 빌드가 제공됩니다.


질문은 "내가 라이브러리를 사용하지 않는다"고 말했다
페미 오니에게

@FemiOni 질문에는 라이브러리에 대한 것이 없습니다 (오래된 편집에서도) ... 여기에있는 몇 가지 다른 답변도 하나 또는 다른 라이브러리를 사용하고 있습니다.
CPHPython

@FemiOni는 어제 반대 투표했습니다. 나는 궁금하다 ... 어쨌든 이것은 학습 장소이고 누군가가 실제로 딥 클론 자체를 구현할 경우 lodash 소스 코드baseClone 가 몇 가지 아이디어를 제공 할 수 있습니다.
CPHPython

@FemiOni JSON 개체는 라이브러리도 프레임 워크도 아닙니다 ... 함수를 구현하려는 경우 오픈 소스 라이브러리 중 하나를 살펴보고 필요한 부분을 사용하는 것이 좋습니다 (많은 수년 동안 테스트를 거쳤습니다). 장기적으로 버그와 누락 된 고려 사항을 피할 수 있습니다.
CPHPython

2

아래 함수는 자바 스크립트 객체를 딥 클론하는 가장 효율적인 방법입니다.

function deepCopy(obj){
    if (!obj || typeof obj !== "object") return obj;

    var retObj = {};

    for (var attr in obj){
        var type = obj[attr];

        switch(true){
            case (type instanceof Date):
                var _d = new Date();
                _d.setDate(type.getDate())
                retObj[attr]= _d;
                break;

            case (type instanceof Function):
                retObj[attr]= obj[attr];
                break;

            case (type instanceof Array):
                var _a =[];
                for (var e of type){
                    //_a.push(e);
                    _a.push(deepCopy(e));
                }
                retObj[attr]= _a;
                break;

            case (type instanceof Object):
                var _o ={};
                for (var e in type){
                    //_o[e] = type[e];
                    _o[e] = deepCopy(type[e]);
                }
                retObj[attr]= _o;
                break;

            default:
                retObj[attr]= obj[attr];
        }
    }
    return retObj;
}

var obj = {
    string: 'test',
    array: ['1'],
    date: new Date(),
    object:{c: 2, d:{e: 3}},
    function: function(){
        return this.date;
    }
};

var copyObj = deepCopy(obj);

console.log('object comparison', copyObj === obj); //false
console.log('string check', copyObj.string === obj.string); //true
console.log('array check', copyObj.array === obj.array); //false
console.log('date check', copyObj2.date === obj.date); //false
console.log('object check', copyObj.object === obj.object); //false
console.log('function check', copyObj.function() === obj.function()); //true

2
귀하의 주장을 뒷받침 할 주장이 있습니까?
Fabian von Ellerts

함수 아래에 예를 들었습니다. 의심이 있습니까?
Kooldandy

이러한 예는 기능이 작동하고 있음을 보여줍니다. 멋지지만 "가장 효율적인 방법"인 이유는 무엇입니까?
Fabian von Ellerts

단일 루프에서 객체의 속성을 재귀 적으로 복사하기 때문입니다. 또한 날짜, 함수, 개체, 배열, 숫자, 문자열 모든 항목이 올바르게 처리됩니다. 다른 방법이 있습니까?
Kooldandy

2

더 이상 그러한 기능이 실제로 필요하지 않아야합니다. 이것은 단순한 학문적 관심사입니다.

순전히 운동이기 때문에 더 기능적인 방법입니다. 그것은의 확장입니다 @ tfmontague의 대답 으로 내가 제안했던 가드 블록이 추가. 그러나 내가 ES6에 강요하고 모든 것을 기능화한다고 느끼는 것을 보면, 여기 내 포주 버전이 있습니다. 배열을 매핑하고 객체를 줄여야하므로 논리가 복잡해 지지만 변형은 방지됩니다.

const cloner = (x) => {
    const recurseObj = x => (typeof x === 'object') ? cloner(x) : x
    const cloneObj = (y, k) => {
        y[k] = recurseObj(x[k])
        return y
    }
    // Guard blocks
    // Add extra for Date / RegExp if you want
    if (!x) {
        return x
    }
    if (Array.isArray(x)) {
        return x.map(recurseObj)
    }
    return Object.keys(x).reduce(cloneObj, {})
}
const tests = [
    null,
    [],
    {},
    [1,2,3],
    [1,2,3, null],
    [1,2,3, null, {}],
    [new Date('2001-01-01')], // FAIL doesn't work with Date
    {x:'', y: {yx: 'zz', yy: null}, z: [1,2,3,null]},
    {
        obj : new function() {
            this.name = "Object test";
        }
    } // FAIL doesn't handle functions
]
tests.map((x,i) => console.log(i, cloner(x)))


1

Map은 특별한 처리가 필요하므로이 스레드의 모든 제안을 통해 코드는 다음과 같습니다.

function deepClone( obj ) {
    if( !obj || true == obj ) //this also handles boolean as true and false
        return obj;
    var objType = typeof( obj );
    if( "number" == objType || "string" == objType ) // add your immutables here
        return obj;
    var result = Array.isArray( obj ) ? [] : !obj.constructor ? {} : new obj.constructor();
    if( obj instanceof Map )
        for( var key of obj.keys() )
            result.set( key, deepClone( obj.get( key ) ) );
    for( var key in obj )
        if( obj.hasOwnProperty( key ) )
            result[key] = deepClone( obj[ key ] );
    return result;
}

1

모든 답변에 대한 나의 추가

deepCopy = arr => {
  if (typeof arr !== 'object') return arr
  if(arr.pop) return [...arr].map(deepCopy)
  const copy = {}
  for (let prop in arr)
    copy[prop] = deepCopy(arr[prop])
  return copy
}

0

이것은 배열, 객체 및 기본 요소에 대해 작동합니다. 두 개의 순회 방법 사이를 전환하는 이중 재귀 알고리즘 :

const deepClone = (objOrArray) => {

  const copyArray = (arr) => {
    let arrayResult = [];
    arr.forEach(el => {
        arrayResult.push(cloneObjOrArray(el));
    });
    return arrayResult;
  }

  const copyObj = (obj) => {
    let objResult = {};
    for (key in obj) {
      if (obj.hasOwnProperty(key)) {
        objResult[key] = cloneObjOrArray(obj[key]);
      }
    }
    return objResult;
  }

  const cloneObjOrArray = (el) => {
    if (Array.isArray(el)) {
      return copyArray(el);
    } else if (typeof el === 'object') {
      return copyObj(el);
    } else {
      return el;
    }
  }

  return cloneObjOrArray(objOrArray);
}

0

우리는 deepCopy를 만들기 위해 재귀를 활용할 수 있습니다. 배열, 객체, 객체 배열, 기능이있는 객체의 복사본을 생성 할 수 있습니다. 원하는 경우지도 등과 같은 다른 유형의 데이터 구조에 대한 기능을 추가 할 수 있습니다.

function deepClone(obj) {
         var retObj;
        _assignProps = function(obj, keyIndex, retObj) {
               var subType = Object.prototype.toString.call(obj[keyIndex]);
               if(subType === "[object Object]" || subType === "[object Array]") {
                    retObj[keyIndex] = deepClone(obj[keyIndex]);
               }
               else {
                     retObj[keyIndex] = obj[keyIndex];
               }
        };

        if(Object.prototype.toString.call(obj) === "[object Object]") {
           retObj = {};
           for(key in obj) {
               this._assignProps(obj, key, retObj);
           }
        }
        else if(Object.prototype.toString.call(obj) == "[object Array]") {
           retObj = [];
           for(var i = 0; i< obj.length; i++) {
              this._assignProps(obj, i, retObj);
            }
        };

        return retObj;
    };

0

immutableJS 사용

import { fromJS } from 'immutable';

// An object we want to clone
let objA = { 
   a: { deep: 'value1', moreDeep: {key: 'value2'} } 
};

let immB = fromJS(objA); // Create immutable Map
let objB = immB.toJS(); // Convert to plain JS object

console.log(objA); // Object { a: { deep: 'value1', moreDeep: {key: 'value2'} } }
console.log(objB); // Object { a: { deep: 'value1', moreDeep: {key: 'value2'} } }

// objA and objB are equalent, but now they and their inner objects are undependent
console.log(objA === objB); // false
console.log(objA.a === objB.a); // false
console.log(objA.moreDeep === objB.moreDeep); // false

또는 lodash / merge

import merge from 'lodash/merge'

var objA = {
    a: [{ 'b': 2 }, { 'd': 4 }]
};
// New deeply cloned object:
merge({}, objA ); 

// We can also create new object from several objects by deep merge:
var objB = {
    a: [{ 'c': 3 }, { 'e': 5 }]
};
merge({}, objA , objB ); // Object { a: [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }

0

이것은 순환 참조를 사용하여 나를 위해 작동합니다.

 //a test-object with circular reference :
 var n1 = {   id:0,   text:"aaaaa",   parent:undefined} 
 var n2 = {  id:1,   text:"zzzzz",   parent:undefined } 
 var o = { arr:[n1,n2],   parent:undefined } 
 n1.parent = n2.parent = o;
 var obj = {   a:1,   b:2,   o:o }
 o.parent = obj;

 function deepClone(o,output){ 

     if(!output) output = {};  
     if(o.______clone) return o.______clone;
     o.______clone = output.______clone = output;

   for(var z in o){

     var obj = o[z];
     if(typeof(obj) == "object") output[z] = deepClone(obj)
     else output[z] = obj; 
    }

   return output;
}

console.log(deepClone(obj));

0

var newDate = new Date (this.oldDate); 나는 oldDate를 함수에 전달하고 this.oldDate에서 newDate를 생성했지만 this.oldDate도 변경하고 있었기 때문에 그 솔루션을 사용했고 작동했습니다.


0

이 솔루션은 [... target] 또는 {... target}을 사용할 때 재귀 문제를 방지합니다.

function shallowClone(target) {
  if (typeof a == 'array') return [...target]
  if (typeof a == 'object') return {...target}
  return target
}

/* set skipRecursion to avoid throwing an exception on recursive references */
/* no need to specify refs, or path -- they are used interally */
function deepClone(target, skipRecursion, refs, path) {
  if (!refs) refs = []
  if (!path) path = ''
  if (refs.indexOf(target) > -1) {
    if (skipRecursion) return null
    throw('Recursive reference at ' + path)
  }
  refs.push(target)
  let clone = shallowCopy(target)
  for (i in target) target[i] = deepClone(target, refs, path + '.' + i)
  return clone
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.