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


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.