3750 Stimmen

Wie kann ich ein JavaScript-Objekt korrekt klonen?

Ich habe ein Objekt x . Ich würde es gerne als Objekt kopieren y , so dass Änderungen an y nicht verändern x . Ich habe festgestellt, dass das Kopieren von Objekten, die von integrierten JavaScript-Objekten abgeleitet sind, zu zusätzlichen, unerwünschten Eigenschaften führt. Das ist kein Problem, da ich eines meiner eigenen, wörtlich konstruierten Objekte kopiere.

Wie kann ich ein JavaScript-Objekt korrekt klonen?

35 Stimmen

0 Stimmen

Unterstützen Sie auf jeden Fall @Niyaz! Shortlink: tinyurl.com/JSCopyObject

290 Stimmen

Für JSON verwende ich mObj=JSON.parse(JSON.stringify(jsonObject));

149voto

Eugene Tiurin Punkte 3709

Ein eleganter Weg, ein Javascript-Objekt in einer Zeile Code zu klonen

Eine Object.assign Methode ist Teil des ECMAScript 2015 (ES6)-Standards und tut genau das, was Sie brauchen.

var clone = Object.assign({}, obj);

Die Methode Object.assign() wird verwendet, um die Werte aller aufzählbaren eigenen Eigenschaften von einem oder mehreren Quellobjekten in ein Zielobjekt zu kopieren.

Lesen Sie mehr...

Le site Polyfill um ältere Browser zu unterstützen:

if (!Object.assign) {
  Object.defineProperty(Object, 'assign', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function(target) {
      'use strict';
      if (target === undefined || target === null) {
        throw new TypeError('Cannot convert first argument to object');
      }

      var to = Object(target);
      for (var i = 1; i < arguments.length; i++) {
        var nextSource = arguments[i];
        if (nextSource === undefined || nextSource === null) {
          continue;
        }
        nextSource = Object(nextSource);

        var keysArray = Object.keys(nextSource);
        for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
          var nextKey = keysArray[nextIndex];
          var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
          if (desc !== undefined && desc.enumerable) {
            to[nextKey] = nextSource[nextKey];
          }
        }
      }
      return to;
    }
  });
}

56 Stimmen

Wird nur ein oberflächliches "Klonen" durchgeführt

0 Stimmen

Ich habe auf die harte Tour gelernt, dass objA = objB; verursacht alle Arten von Kopfschmerzen. Damit scheint das Problem gelöst zu sein, zumindest im Moment...

143voto

itpastorn Punkte 2867

Es gibt viele Antworten, aber keine, in der erwähnt wird Objekt.erstellen aus ECMAScript 5, das zwar keine exakte Kopie liefert, aber die Quelle als Prototyp des neuen Objekts festlegt.

Dies ist also keine exakte Antwort auf die Frage, aber es ist eine Ein-Zeilen-Lösung und daher elegant. Und sie funktioniert am besten für 2 Fälle:

  1. Wo eine solche Vererbung nützlich ist (duh!)
  2. Das Quellobjekt wird nicht verändert, so dass die Beziehung zwischen den beiden Objekten keine Rolle spielt.

Exemple :

var foo = { a : 1 };
var bar = Object.create(foo);
foo.a; // 1
bar.a; // 1
foo.a = 2;
bar.a; // 2 - prototype changed
bar.a = 3;
foo.a; // Still 2, since setting bar.a makes it an "own" property

Warum halte ich diese Lösung für die bessere? Sie ist nativ, also keine Schleifen, keine Rekursion. Allerdings benötigen ältere Browser ein Polyfill.

116 Stimmen

Dies ist eine prototypische Vererbung, kein Klonen. Das sind zwei völlig verschiedene Dinge. Das neue Objekt hat keine eigenen Eigenschaften, es verweist nur auf die Eigenschaften des Prototyps. Der Sinn des Klonens ist es, ein neues Objekt zu erstellen, das keine Eigenschaften eines anderen Objekts referenziert.

101voto

Fabio Poloni Punkte 7891

Bei den meisten Lösungen im Internet gibt es mehrere Probleme. Daher habe ich mich entschlossen, einen Nachtrag zu verfassen, in dem ich darlege, warum die akzeptierte Antwort nicht akzeptiert werden sollte.

Ausgangssituation

Quiero Deep-Copy ein Javascript Object mit all seinen Kindern und deren Kindern und so weiter. Aber da ich kein normaler Entwickler bin, ist mein Object tiene normal properties , circular structures und sogar nested objects .

Erstellen wir also eine circular structure und eine nested object Erstens.

function Circ() {
    this.me = this;
}

function Nested(y) {
    this.y = y;
}

Bringen wir alles zusammen in einem Object namens a .

var a = {
    x: 'a',
    circ: new Circ(),
    nested: new Nested('a')
};

Als nächstes wollen wir kopieren a in eine Variable namens b und mutieren sie.

var b = a;

b.x = 'b';
b.nested.y = 'b';

Sie wissen, was hier passiert ist, denn sonst wären Sie gar nicht auf diese tolle Frage gestoßen.

console.log(a, b);

a --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

Jetzt müssen wir eine Lösung finden.

JSON

Der erste Versuch, den ich unternahm, war die Verwendung von JSON .

var b = JSON.parse( JSON.stringify( a ) );

b.x = 'b';
b.nested.y = 'b';

Verschwenden Sie nicht zu viel Zeit damit, Sie werden TypeError: Converting circular structure to JSON .

Rekursive Kopie (die akzeptierte "Antwort")

Werfen wir einen Blick auf die akzeptierte Antwort.

function cloneSO(obj) {
    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = cloneSO(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        var copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = cloneSO(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}

Sieht gut aus, nicht wahr? Es ist eine rekursive Kopie des Objekts und verarbeitet auch andere Typen, wie Date aber das war keine Bedingung.

var b = cloneSO(a);

b.x = 'b';
b.nested.y = 'b';

Rekursion und circular structures funktioniert nicht gut zusammen... RangeError: Maximum call stack size exceeded

native Lösung

Nachdem ich mich mit meinem Kollegen gestritten hatte, fragte mein Chef uns, was passiert sei, und er fand eine einfache Lösung Lösung nach einigem Googeln. Es heißt Object.create .

var b = Object.create(a);

b.x = 'b';
b.nested.y = 'b';

Diese Lösung wurde vor einiger Zeit zu Javascript hinzugefügt und behandelt sogar circular structure .

console.log(a, b);

a --> Object {
    x: "a",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

... und Sie sehen, dass es mit der verschachtelten Struktur darin nicht funktioniert hat.

Polyfill für die native Lösung

Es gibt ein Polyfill für Object.create in älteren Browsern wie dem IE 8. Es ist so etwas wie eine Empfehlung von Mozilla, und natürlich ist es nicht perfekt und führt zu dem gleichen Problem wie der native Lösung .

function F() {};
function clonePF(o) {
    F.prototype = o;
    return new F();
}

var b = clonePF(a);

b.x = 'b';
b.nested.y = 'b';

Ich habe die F außerhalb des Geltungsbereichs, damit wir einen Blick darauf werfen können, was instanceof sagt uns.

console.log(a, b);

a --> Object {
    x: "a",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> F {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

console.log(typeof a, typeof b);

a --> object
b --> object

console.log(a instanceof Object, b instanceof Object);

a --> true
b --> true

console.log(a instanceof F, b instanceof F);

a --> false
b --> true

Das gleiche Problem wie bei der native Lösung aber eine etwas schlechtere Leistung.

die bessere (aber nicht perfekte) Lösung

Beim Stöbern habe ich eine ähnliche Frage gefunden ( In Javascript, bei der Durchführung einer tiefen Kopie, wie kann ich vermeiden, einen Zyklus, aufgrund einer Eigenschaft, die "this"? ) zu diesem, aber mit einer viel besseren Lösung.

function cloneDR(o) {
    const gdcc = "__getDeepCircularCopy__";
    if (o !== Object(o)) {
        return o; // primitive value
    }

    var set = gdcc in o,
        cache = o[gdcc],
        result;
    if (set && typeof cache == "function") {
        return cache();
    }
    // else
    o[gdcc] = function() { return result; }; // overwrite
    if (o instanceof Array) {
        result = [];
        for (var i=0; i<o.length; i++) {
            result[i] = cloneDR(o[i]);
        }
    } else {
        result = {};
        for (var prop in o)
            if (prop != gdcc)
                result[prop] = cloneDR(o[prop]);
            else if (set)
                result[prop] = cloneDR(cache);
    }
    if (set) {
        o[gdcc] = cache; // reset
    } else {
        delete o[gdcc]; // unset again
    }
    return result;
}

var b = cloneDR(a);

b.x = 'b';
b.nested.y = 'b';

Werfen wir einen Blick auf die Ausgabe...

console.log(a, b);

a --> Object {
    x: "a",
    circ: Object {
        me: Object { ... }
    },
    nested: Object {
        y: "a"
    }
}

b --> Object {
    x: "b",
    circ: Object {
        me: Object { ... }
    },
    nested: Object {
        y: "b"
    }
}

console.log(typeof a, typeof b);

a --> object
b --> object

console.log(a instanceof Object, b instanceof Object);

a --> true
b --> true

console.log(a instanceof F, b instanceof F);

a --> false
b --> false

Die Anforderungen sind angepasst, aber es gibt noch einige kleinere Probleme, wie die Änderung der instance von nested y circ a Object .

Die Struktur von Bäumen, die sich ein Blatt teilen, wird nicht kopiert, sie werden zu zwei unabhängigen Blättern:

        [Object]                     [Object]
         /    \                       /    \
        /      \                     /      \
      |/_      _\|                 |/_      _\|  
  [Object]    [Object]   ===>  [Object]    [Object]
       \        /                 |           |
        \      /                  |           |
        _\|  |/_                 \|/         \|/
        [Object]               [Object]    [Object]

Schlussfolgerung

Die letzte Lösung, die eine Rekursion und einen Cache verwendet, ist vielleicht nicht die beste, aber sie ist eine real Deep-Copy des Objekts. Es behandelt einfache properties , circular structures y nested object aber es wird die Instanz von ihnen beim Klonen durcheinander bringen.

jsfiddle

12 Stimmen

Der Zusammenschluss dient also dazu, dieses Problem zu vermeiden :)

0 Stimmen

@mikus bis es eine real Spezifikation, die mehr als nur die grundlegenden Anwendungsfälle abdeckt, ja.

2 Stimmen

Die Analyse der oben genannten Lösungen ist in Ordnung, aber die Schlussfolgerung des Autors zeigt, dass es keine Lösung für diese Frage gibt.

80voto

dule Punkte 17160

Wenn Sie mit einer oberflächlichen Kopie zurechtkommen, bietet die underscore.js-Bibliothek eine klonen. méthode.

y = _.clone(x);

oder Sie können es erweitern wie

copiedObject = _.extend({},originalObject);

2 Stimmen

Danke! Verwenden Sie diese Technik auf einem Meteor-Server.

0 Stimmen

Um schnell mit lodash loszulegen, empfehle ich, npm, Browserify und lodash zu lernen. Ich habe clone mit 'npm i --save lodash.clone' zum Laufen gebracht und dann 'var clone = require('lodash.clone');' Um require zum Laufen zu bringen, brauchen Sie etwas wie browserify. Sobald Sie es installiert und gelernt haben, wie es funktioniert, werden Sie jedes Mal, wenn Sie Ihren Code ausführen (anstatt Chrome direkt aufzurufen), "browserify yourfile.js > bundle.js;start chrome index.html" verwenden. Dadurch werden Ihre Datei und alle Dateien, die Sie aus dem npm-Modul benötigen, in bundle.js zusammengefasst. Sie können wahrscheinlich Zeit sparen und automatisieren diesen Schritt mit Gulp though.

78voto

Alireza Punkte 92209

GUT, Stellen Sie sich vor, Sie haben dieses Objekt unten und wollen es klonen:

let obj = {a:1, b:2, c:3}; //ES6

o

var obj = {a:1, b:2, c:3}; //ES5

die Antwort hängt hauptsächlich davon ab, welche ECMAscript die Sie verwenden, in ES6+ können Sie einfach Folgendes verwenden Object.assign um den Klon zu erstellen:

let cloned = Object.assign({}, obj); //new {a:1, b:2, c:3};

oder mit einem Spread-Operator wie diesem:

let cloned = {...obj}; //new {a:1, b:2, c:3};

Aber wenn Sie mit ES5 können Sie einige Methoden anwenden, aber die JSON.stringify Stellen Sie einfach sicher, dass Sie nicht für einen großen Brocken von Daten zu kopieren, aber es könnte eine Zeile praktische Art und Weise in vielen Fällen, so etwas wie dieses sein:

let cloned = JSON.parse(JSON.stringify(obj)); 
//new {a:1, b:2, c:3};, can be handy, but avoid using on big chunk of data over and over

0 Stimmen

Können Sie bitte ein Beispiel dafür geben, was big chunk of data entsprechen würde? 100kb? 100MB? Vielen Dank!

0 Stimmen

Ja, @user1063287, dass im Grunde die größeren Daten, die performnce schlechter... so es wirklich abhängt, nicht ein kb, mb oder gb, es ist mehr über wie viele Male Sie das auch tun wollen... Außerdem wird es nicht für Funktionen und andere Dinge funktionieren...

3 Stimmen

Object.assign macht eine flache Kopie (genau wie die Ausbreitung, @Alizera)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X