Wie man tief in Javascript klont

105

Wie kann man ein Javascript-Objekt tief klonen?

Ich weiß , gibt es verschiedene Funktionen auf Basis von Frameworks wie JSON.parse(JSON.stringify(o))und $.extend(true, {}, o)aber ich möchte nicht , einen Rahmen, so verwenden.

Was ist der eleganteste oder effizienteste Weg, um einen tiefen Klon zu erstellen?

Wir kümmern uns um Randfälle wie das Klonen von Arrays. Prototypenketten nicht brechen, sich mit Selbstreferenz befassen.

Es ist uns nicht wichtig, das Kopieren von DOM-Objekten und Ähnlichem zu unterstützen, da .cloneNodees aus diesem Grund existiert.

Da ich hauptsächlich tiefe Klone für die node.jsVerwendung der ES5-Funktionen der V8-Engine verwenden möchte, ist dies akzeptabel.

[Bearbeiten]

Bevor jemand vorschlägt, möchte ich erwähnen, dass es einen deutlichen Unterschied zwischen dem Erstellen einer Kopie durch prototypisches Erben vom Objekt und dem Klonen gibt . Ersteres bringt die Prototypenkette durcheinander.

[Weiter bearbeiten]

Nachdem ich Ihre Antwort gelesen hatte, kam ich zu der nervigen Entdeckung, dass das Klonen ganzer Objekte ein sehr gefährliches und schwieriges Spiel ist. Nehmen Sie zum Beispiel das folgende schließungsbasierte Objekt

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

Gibt es eine Möglichkeit, eine Klonfunktion zu schreiben, die das Objekt klont, zum Zeitpunkt des Klonens denselben Status hat, den Status jedoch nicht ändern kann, oohne einen JS-Parser in JS zu schreiben?

Es sollte keine reale Welt mehr für eine solche Funktion geben. Dies ist nur akademisches Interesse.

Raynos
quelle
2
Bevor es als doppelt markiert wird, habe ich mir stackoverflow.com/questions/122102/… angesehen und keine Antwort gefunden, die sich mit allen Randfällen befasste.
Raynos
Die Anforderung im Abschnitt "Weitere Bearbeitung" kann ohne "Hilfe" des Objekts selbst nicht erfüllt werden , da solche privaten Variablen wirklich privat sind und folglich von einer generischen cloneFunktion nicht zugänglich sind . Das betreffende Objekt sollte eine eigene maßgeschneiderte cloneMethode bereitstellen.
Trincot
Ich habe heute Abend ein paar davon gelesen, und unter den Ressourcen, die ich gefunden habe, befand sich dieser hässlich aussehende Blog-Beitrag, der einige Hacks für den Zugriff auf den strukturierten Klon-Algorithmus im Browser enthält: dassur.ma/things/deep-copy
Katze

Antworten:

66

Es kommt wirklich darauf an, was Sie klonen möchten. Ist dies ein echtes JSON-Objekt oder nur ein Objekt in JavaScript? Wenn Sie einen Klon erstellen möchten, kann dies zu Problemen führen. Welches Problem? Ich werde es unten erklären, aber zuerst ein Codebeispiel, das Objektliterale, alle Grundelemente, Arrays und DOM-Knoten klont.

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

Lassen Sie uns nun über Probleme sprechen, die beim Klonen von REAL-Objekten auftreten können. Ich spreche jetzt von Objekten, die Sie erstellen, indem Sie so etwas tun

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

Natürlich können Sie sie klonen, es ist kein Problem, jedes Objekt macht Konstruktoreigenschaften verfügbar und Sie können sie zum Klonen von Objekten verwenden, aber es funktioniert nicht immer. Sie können for indiese Objekte auch einfach bearbeiten, aber es geht in die gleiche Richtung - Probleme. Ich habe auch Klonfunktionen in den Code aufgenommen, diese werden jedoch durch if( false )Anweisungen ausgeschlossen .

Warum kann das Klonen ein Schmerz sein? Zunächst einmal könnte jedes Objekt / jede Instanz einen bestimmten Status haben. Sie können nie sicher sein, dass Ihre Objekte beispielsweise keine privaten Variablen haben, und wenn dies der Fall ist, brechen Sie durch Klonen von Objekten einfach den Status.

Stellen Sie sich vor, es gibt keinen Staat, das ist in Ordnung. Dann haben wir noch ein anderes Problem. Das Klonen über die "Konstruktor" -Methode gibt uns ein weiteres Hindernis. Es ist eine Argumentabhängigkeit. Sie können nie sicher sein, dass jemand, der dieses Objekt erstellt hat, es nicht getan hat

new User({
   bike : someBikeInstance
});

Wenn dies der Fall ist, haben Sie kein Glück, someBikeInstance wurde wahrscheinlich in einem bestimmten Kontext erstellt und dieser Kontext ist für die Klonmethode nicht bekannt.

Was tun? Sie können immer noch for inLösungen finden und solche Objekte wie normale Objektliterale behandeln, aber vielleicht ist es eine Idee, solche Objekte überhaupt nicht zu klonen und nur die Referenz dieses Objekts zu übergeben?

Eine andere Lösung ist - Sie könnten eine Konvention festlegen, dass alle Objekte, die geklont werden müssen, diesen Teil selbst implementieren und eine geeignete API-Methode (wie cloneObject) bereitstellen sollten. Etwas, was cloneNodefür DOM getan wird.

Du entscheidest.

nemisj
quelle
Ich kam auf die Hürde, mit Objekten umzugehen, die Verschlüsse verwenden, um den Zustand selbst zu verbergen. Wie kann man ein Objekt und seinen gesamten Zustand klonen, aber dennoch sicherstellen, dass der Klon den ursprünglichen Zustand nicht selbst ändern kann? Ein Kopplungspunkt, result = new item.constructor();der schlecht ist, ist, dass Sie angesichts der Konstruktorfunktion und des Elementobjekts in der Lage sein sollten, alle an den Konstruktor übergebenen Parameter zu RE.
Raynos
7
@ Raynos: Wenn Objekte Verschlüsse verwenden, um den Status auszublenden, können Sie sie nicht klonen. Daher der Begriff "Schließung". Wie Nemisj am Ende sagt, besteht der beste Weg darin, eine API-Methode zum Klonen (oder Serialisieren / Deserialisieren) zu implementieren, wenn dies eine Option ist.
Michiel Kalkman
@MichielKalkman Ich hatte das Gefühl, dass dies der Fall war. Obwohl jemand eine wirklich clevere Lösung dafür gehabt haben könnte.
Raynos
Was bewirkt das false && item.constructor? Ist das nicht ifnutzlos?
Gabriel Petrovay
2
@GabrielPetrovay Das ifist aus funktionaler Sicht "nutzlos", weil es niemals ausgeführt wird, aber es hat den akademischen Zweck, eine hypothetische Implementierung zu zeigen, die man versuchen könnte, die der Autor aus dem später erläuterten Grund nicht empfiehlt. Ja, es wird elsejedes Mal eine Klausel auslösen , wenn die Bedingung ausgewertet wird, und dennoch gibt es einen Grund dafür, dass der Code vorhanden ist.
Gui Imamura
154

Sehr einfacher Weg, vielleicht zu einfach:

var cloned = JSON.parse(JSON.stringify(objectToClone));
G. Ghez
quelle
12
Großartig, es sei denn, ein Objektwert ist eine Funktion. An diesem Punkt müssen Sie so etwas wie die akzeptierte Antwort verwenden. Oder verwenden Sie eine cloneDeepHilfsfunktion wie in Lodash.
Matthoiland
31
Wenn ein Objektwert eine Funktion ist, ist das Objekt kein JSON.
Jos de Jong
5
Welcher Anwendungsfall kann es rechtfertigen, eine Funktion zu klonen, anstatt sie nur zu verwenden?
G. Ghez
3
Wenn ich mich richtig erinnere, konvertiert dies auch Daten in Zeichenfolgen.
Peter
3
@ G.Ghez Wenn Sie ein Objekt klonen, das eine Funktion enthält, geht diese Funktion verloren.
Peter
38

Die JSON.parse(JSON.stringify())Kombination zum tiefen Kopieren von Javascript-Objekten ist ein ineffektiver Hack, da er für JSON-Daten gedacht war. Es unterstützt keine Werte von undefinedoder function () {}und ignoriert sie einfach (oder nullsie), wenn das Javascript-Objekt in JSON "stringifiziert" (Marshalling) wird.

Eine bessere Lösung ist die Verwendung einer Deep Copy-Funktion. Die folgende Funktion kopiert Objekte tief und erfordert keine Bibliothek eines Drittanbieters (jQuery, LoDash usw.).

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;
}
tfmontague
quelle
7
Außer wenn ein Objekt (oder ein anderes Objekt, das es enthält) eine Selbstreferenz auf sich selbst enthält ... stackoverflow ™!
e2-e4
@ ringø - Können Sie einige "Selbstreferenz" -Testfälle bereitstellen?
tfmontague
4
var o = { a:1, b:2 } ; o["oo"] = { c:3, m:o };
e2-e4
4
Ich mag diese Lösung. Die einzige Lösung für mich war der Umgang mit Nullwerten: bObject[k] = (v === null) ? null : (typeof v === "object") ? copy(v) : v;
David Kirkland
2
Diese Funktion ist einfach, verständlich und erfasst fast alle Fälle. In der JavaScript-Welt ist das so perfekt wie möglich.
icc97
20

Hier ist eine ES6-Funktion, die auch für Objekte mit zyklischen Referenzen funktioniert:

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

Ein Hinweis zu Sets und Maps

Wie mit den Tasten der Sätze behandeln und Karten ist umstritten: die Schlüssel sind oft Primitiven (in diesem Fall gibt es keine Debatte), aber sie können auch Objekte sein. In diesem Fall stellt sich die Frage: Sollten diese Schlüssel geklont werden?

Man könnte argumentieren, dass dies getan werden sollte, so dass, wenn diese Objekte in der Kopie mutiert sind, die Objekte im Original nicht betroffen sind und umgekehrt.

Auf der anderen Seite möchte man, dass, wenn ein hasSchlüssel festgelegt / zugeordnet wird, dies sowohl im Original als auch in der Kopie zutrifft - zumindest bevor Änderungen an einem der beiden vorgenommen werden. Es wäre seltsam, wenn die Kopie ein Set / Map mit Schlüsseln wäre, die noch nie zuvor aufgetreten sind (wie sie während des Klonvorgangs erstellt wurden): Dies ist sicherlich nicht sehr nützlich für Code, der wissen muss, ob ein bestimmtes Objekt ein Objekt ist Geben Sie das Set / Map ein oder nicht.

Wie Sie bemerken, bin ich eher der zweiten Meinung: Die Schlüssel von Sets und Maps sind Werte (möglicherweise Referenzen ), die gleich bleiben sollten.

Solche Auswahlmöglichkeiten tauchen häufig auch bei anderen (möglicherweise benutzerdefinierten) Objekten auf. Es gibt keine allgemeine Lösung, da viel davon abhängt, wie sich das geklonte Objekt in Ihrem speziellen Fall verhalten soll.

Trincot
quelle
behandelt Datum und Regexp nicht
mkeremguc
1
@mkeremguc, danke für deinen Kommentar. Ich habe den Code aktualisiert, um Datum und regulären Ausdruck zu unterstützen.
Trincot
Schöne Lösung inklusive Karte. Es fehlt nur die Unterstützung für das Klonen von es6-Sets.
Robert Biggs
Wenn ich mich nicht irre, können Sie wahrscheinlich Unterstützung für Sets hinzufügen mit:if (object instanceof Set) Array.from(object, val => result.add(deepClone(val, hash)));
Robert Biggs
@ RobertBiggs, das ist eine Möglichkeit, aber meiner Meinung nach sollte ein Set, das einen bestimmten Schlüssel hat, auch in der geklonten Version dieses Sets zutreffen. Mit Ihrem vorgeschlagenen Code würde dies nicht zutreffen, wenn die Schlüssel Objekte sind. Daher würde ich vorschlagen, die Schlüssel nicht zu klonen - ich denke wirklich, dass es sich dann mehr wie erwartet verhalten würde. Siehe diesbezügliche Aktualisierung meiner Antwort.
Trincot
10

Die Contrib-Bibliotheksbibliothek von Underscore.js verfügt über eine Funktion namens Snapshot , mit der ein Objekt tief geklont wird

Ausschnitt aus der Quelle:

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

Sobald die Bibliothek mit Ihrem Projekt verknüpft ist, rufen Sie die Funktion einfach mit auf

_.snapshot(object);
svarog
quelle
4
Gute Lösung, nur ein Punkt, an den Sie sich erinnern sollten: Der Klon und das Original haben denselben Prototyp. Wenn dies ein Problem ist, können Sie "temp .__ proto__ = .snapshot (obj .__ proto_ )" hinzufügen . direkt über dem "return temp" und um integrierte Klassen mit Eigenschaften zu unterstützen, die als "no enumerate" markiert sind, können Sie getOwnPropertyNames () anstelle von "for (var key in obj)" iterieren
Ronen Ness
3

Dies ist die Deep-Cloning-Methode, die ich verwende. Ich finde sie großartig. Ich hoffe, Sie machen Vorschläge

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;
}
Stich
quelle
3

Wie andere zu dieser und ähnlichen Fragen bemerkt haben, ist das Klonen eines "Objekts" im allgemeinen Sinne in JavaScript zweifelhaft.

Es gibt jedoch eine Klasse von Objekten, die ich "Daten" -Objekte nenne, { ... }dh solche, die einfach aus Literalen und / oder einfachen Eigenschaftszuweisungen erstellt oder aus JSON deserialisiert wurden, für die es sinnvoll ist, klonen zu wollen. Gerade heute wollte ich die von einem Server empfangenen Daten künstlich um das Fünffache aufblasen, um zu testen, was für einen großen Datensatz passiert, aber das Objekt (ein Array) und seine untergeordneten Objekte mussten unterschiedliche Objekte sein, damit die Dinge richtig funktionieren. Durch Klonen konnte ich dies tun, um meinen Datensatz zu multiplizieren:

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

Der andere Ort, an dem ich häufig Datenobjekte klone, ist das Zurücksenden von Daten an den Host, wo ich vor dem Senden Statusfelder vom Objekt im Datenmodell entfernen möchte. Zum Beispiel möchte ich möglicherweise alle Felder, die mit "_" beginnen, vom geklonten Objekt entfernen.

Dies ist der Code, den ich geschrieben habe, um dies generisch zu tun, einschließlich unterstützender Arrays und eines Selektors, um auszuwählen, welche Mitglieder geklont werden sollen (der eine "Pfad" -String verwendet, um den Kontext zu bestimmen):

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

Die einfachste vernünftige Deep-Clone-Lösung unter der Annahme eines Nicht-Null-Root-Objekts und ohne Elementauswahl ist:

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;
    }
Lawrence Dol
quelle
2

Lo-Dash , jetzt eine Obermenge von Underscore.js , verfügt über einige Deep-Clone-Funktionen:

Aus einer Antwort des Autors selbst:

lodash underscore Build wird bereitgestellt, um die Kompatibilität mit der neuesten stabilen Version von Underscore sicherzustellen.

CPHPython
quelle
Frage sagte "Ich möchte keine Bibliotheken verwenden"
Femi Oni
@FemiOni Die Frage hat nichts über Bibliotheken (auch nicht in ihren alten Änderungen) ... Einige andere Antworten hier verwenden auch die eine oder andere Bibliothek.
CPHPython
@FemiOni Antwort gestern abgelehnt. Ich frage mich ... Wie auch immer, dies ist ein Lernort und nur für den Fall, dass jemand den Deep Clone selbst implementieren wird, kann der Lodash-QuellcodebaseClone einige Ideen liefern.
CPHPython
@FemiOni Das JSON- Objekt ist weder eine Bibliothek noch ein Framework. Wenn Sie die Funktion implementieren möchten , empfehle ich, in eine der Open Source-Bibliotheken zu schauen und die Teile zu verwenden, die Sie benötigen (viele wurden jahrelang getestet). Es wird langfristig Fehler und fehlende Überlegungen vermeiden.
CPHPython
2

Die folgende Funktion ist der effizienteste Weg, um Javascript-Objekte tief zu klonen.

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
Kooldandy
quelle
2
Haben Sie Argumente, um Ihre Behauptung zu stützen?
Fabian von Ellerts
Ich habe ein Beispiel unter der Funktion gegeben. Hast du irgendwelche Zweifel?
Kooldandy
Diese Beispiele zeigen, dass die Funktion funktioniert, was cool ist, aber warum ist es "der effizienteste Weg"?
Fabian von Ellerts
Weil die Attribute des Objekts rekursiv in einer einzigen Schleife kopiert werden. Auch Datum, Funktion, Objekt, Array, Nummer, Zeichenfolge werden ordnungsgemäß behandelt. Haben Sie einen anderen Weg?
Kooldandy
2

Es sollte keine reale Welt mehr für eine solche Funktion geben. Dies ist nur akademisches Interesse.

Als reine Übung ist dies eine funktionalere Methode. Es ist eine Erweiterung von @ tfmontagues Antwort, da ich vorgeschlagen hatte , dort einen Wachblock hinzuzufügen. Aber da ich mich gezwungen fühle, ES6 zu verwenden und all die Dinge zu funktionalisieren, ist hier meine aufgemotzte Version. Dies verkompliziert die Logik, da Sie das Array zuordnen und über das Objekt reduzieren müssen, vermeidet jedoch Mutationen.

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

icc97
quelle
1

Mir ist aufgefallen, dass Map eine besondere Behandlung erfordern sollte. Bei allen Vorschlägen in diesem Thread lautet der Code daher:

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;
}
Dmitriy Pichugin
quelle
1

meine Ergänzung zu allen Antworten

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
}
Ronald C.
quelle
0

Dies funktioniert für Arrays, Objekte und Grundelemente. Doppelt rekursiver Algorithmus, der zwischen zwei Durchlaufmethoden wechselt:

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);
}
Eric Grotke
quelle
0

Wir können die Rekursion verwenden, um deepCopy zu erstellen. Es kann eine Kopie von Array, Objekt, Array von Objekt, Objekt mit Funktion erstellen. Wenn Sie möchten, können Sie Funktionen für andere Arten von Datenstrukturen wie Karten usw. hinzufügen.

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;
    };
Niemand
quelle
0

Verwenden Sie unveränderliche JS

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

Oder 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 }] }
Alexander Ladonin
quelle
0

Dieser mit Zirkelverweis funktioniert für mich

 //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));
Tom Lecoz
quelle
0

var newDate = neues Datum (this.oldDate); Ich habe oldDate an function übergeben und newDate aus this.oldDate generiert, aber es hat this.oldDate auch geändert. Also habe ich diese Lösung verwendet und es hat funktioniert.

Tejas Pawar
quelle
0

Diese Lösung vermeidet Rekursionsprobleme bei Verwendung von [... Ziel] oder {... Ziel}

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
}
pbatey
quelle