Frage

Ich habe ein Objekt, x.Ich möchte es als Objekt kopieren y, so dass sich zu ändert y nicht ändern x.Mir wurde klar, 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 klone ich ein JavaScript-Objekt richtig?

War es hilfreich?

Lösung

das in JavaScript für jedes Objekt zu tun, wird nicht einfach oder unkompliziert sein. Sie werden von dem Objekt Prototyp fälschlicherweise Aufnehmen Attribute in das Problem ausführen, die in dem Prototyp überlassen werden sollen und nicht auf die neue Instanz kopiert. Wenn, zum Beispiel, werden Sie eine clone Methode Hinzufügen Object.prototype, wie einige Antworten zeigen, müssen Sie explizit das Attribut überspringen. Aber was, wenn es andere zusätzliche Methoden hinzugefügt Object.prototype oder andere Zwischen Prototypen, dass man nicht über sie wissen? In diesem Fall werden Sie Attribute kopieren Sie sollten nicht, so müssen Sie unvorhergesehene, nicht-lokale Attribute mit dem hasOwnProperty Methode

Neben nicht-zählbaren Attribute, werden Sie ein härteres Problem auftreten, wenn Sie versuchen, Objekte zu kopieren, die Eigenschaften versteckt hat. Zum Beispiel ist prototype eine versteckte Eigenschaft einer Funktion. Außerdem wird ein Objekt der Prototyp mit dem Attribut __proto__ verwiesen, die auch versteckt ist, und wird nicht durch eine für / Iterieren in Schleife über die Attribute des Quellobjekt kopiert werden. Ich denke, __proto__ spezifisch sein könnte Firefox JavaScript-Interpreter und es kann etwas anderes in anderen Browsern, aber Sie erhalten das Bild. Nicht alles ist abzählbar. Sie können ein verstecktes Attribut kopieren, wenn Sie seinen Namen kennen, aber ich weiß nicht von irgendeiner Weise automatisch zu entdecken.

Noch ein weiterer Haken bei der Suche nach einer eleganten Lösung ist das Problem der Einstellung korrekt die Prototypvererbung auf. Wenn Ihr Prototyp des Quellobjekts Object, dann die Schaffung einfach ein neues allgemeines Ziel mit {} funktionieren wird, aber wenn die Prototyp der Quelle einige Nachkommen Object ist, dann werden Sie die zusätzlichen Mitglieder von diesem Prototyp zu fehlen, die Sie die hasOwnProperty ausgelassen mit Filter oder die waren im Prototyp, waren aber in erster Linie nicht zählbar. Eine Lösung könnte sein, die Quelle des Objekts constructor Eigenschaft rufen Sie das ursprüngliche Kopie-Objekt zu erhalten und dann über die Attribute zu kopieren, aber dann noch werden Sie nicht nicht-zählbare Attribute erhalten. Zum Beispiel kann ein title="Mozilla JavaScript Reference: Date"> Date rel="noreferrer"

function clone(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    var copy = obj.constructor();
    for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
    }
    return copy;
}

var d1 = new Date();

/* Executes function after 5 seconds. */
setTimeout(function(){
    var d2 = clone(d1);
    alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString());
}, 5000);

Das Datum Zeichenfolge für d1 ist 5 Sekunden hinter der d2. Ein Weg, ein Date gleicht dem anderen zu machen, ist durch die setTime Methode aufrufen, aber das ist spezifisch für die Date Klasse. Ich glaube nicht, gibt es eine kugelsichere allgemeine Lösung für dieses Problem, obwohl ich falsch sein würde glücklich sein!

Wenn ich allgemein tiefes Kopieren implementieren musste ich durch die Annahme zu gefährden endete, dass ich nur ein einfaches Object, Array, Date, String, Number oder Boolean kopieren müsste. Die letzten drei Arten sind unveränderlich, so konnte ich eine flache Kopie durchführen und sich keine Sorgen darüber zu verändern. I weiterhin angenommen, dass alle Elemente in Object oder Array enthielten auch eine der 6 einfachen Typen in dieser Liste sein würden. Dies kann mit dem Code wie folgt erreicht werden:

function clone(obj) {
    var copy;

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

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

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

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

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

Die obige Funktion wird funktionieren angemessen für die 6 einfachen Typen I erwähnt, solange die Daten in den Objekten und Arrays, die eine Baumstruktur bilden. Das heißt, es auf die gleichen Daten in dem Objekt nicht mehr als eine Referenz. Zum Beispiel:

// This would be cloneable:
var tree = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "right" : null,
    "data"  : 8
};

// This would kind-of work, but you would get 2 copies of the 
// inner node instead of 2 references to the same copy
var directedAcylicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
directedAcyclicGraph["right"] = directedAcyclicGraph["left"];

// Cloning this would cause a stack overflow due to infinite recursion:
var cyclicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
cyclicGraph["right"] = cyclicGraph;

Es wird nicht in der Lage sein, jedes JavaScript-Objekt zu behandeln, aber es kann für viele p ausreichend sein,urposes solange man nicht davon ausgehen, dass es für alles, was es nur Arbeit werden Sie werfen.

Andere Tipps

Mit jQuery können Sie flache Kopie mit erweitern :

var copiedObject = jQuery.extend({}, originalObject)

nachträgliche Änderungen des copiedObject nicht die originalObject beeinflussen, und umgekehrt.

oder eine tiefe Kopie zu machen :

var copiedObject = jQuery.extend(true, {}, originalObject)

In ECMAScript 6 gibt es Object.assign Methode , die kopiert Werte aller enumerable eigenen Eigenschaften von einem Objekt zum anderen. Zum Beispiel:

var x = {myProp: "value"};
var y = Object.assign({}, x); 

Aber beachten Sie, dass verschachtelte Objekte als Referenz noch kopiert werden.

MDN :

  • Wenn Sie flache Kopie, Verwendung Object.assign({}, a)
  • Für "tief" zu kopieren, verwenden JSON.parse(JSON.stringify(a))

Es besteht keine Notwendigkeit für externe Bibliotheken aber man braucht ein href zu überprüfen <= „https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Browser_compatibility“ rel = "noreferrer"> Browser-Kompatibilität zuerst .

Es gibt viele Antworten, aber keine, die Object.create von ECMAScript 5, die zwar nicht eine exakte Kopie nicht geben, sondern stellt die Quelle als der Prototyp des neuen Objekts.

Somit ist dies nicht eine genaue Antwort auf die Frage, aber es ist eine einzeilige Lösung und damit elegant. Und es funktioniert am besten für 2 Fälle:

  1. Wenn eine solche Erbschaft ist nützlich (duh!)
  2. Wenn das Quellobjekt nicht geändert werden, so dass die Beziehung zwischen den zwei Objekten kein Thema zu machen.

Beispiel:

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 überlegen sein? Es ist heimisch, also kein Looping, keine Rekursion. Allerdings müssen ältere Browser ein polyfill.

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

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

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

Die Object.assign () Methode wird verwendet, um die Werte aller enumerable eigenen Eigenschaften von einem oder mehreren Quellobjekte zu einem Zielobjekt zu kopieren.

Lesen Sie mehr ...

Die polyfill ältere Browser 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;
    }
  });
}

Es gibt einige Probleme mit den meisten Lösungen im Internet. Also beschloss ich, ein Follow-up zu machen, die beinhalten, warum die akzeptierte Antwort nicht akzeptiert werden soll.

Ausgangssituation

Ich will Tief Kopie ein Javascript Object mit all seinen Kindern und ihre Kinder und so weiter. Da ich aber nicht Art eines normalen Entwickler bin, mein Object hat normale properties, circular structures und sogar nested objects.

Lassen Sie uns also einen circular structure erstellen und eine nested object erste.

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

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

Lassen Sie sich alles zusammen bringen in einer Object namens a.

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

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

var b = a;

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

Sie wissen, was hier passiert ist, denn wenn nicht du nicht einmal auf dieser großen Frage landen würde.

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

Nun wollen wir eine Lösung finden.

JSON

Der erste Versuch habe ich versucht war JSON verwendet wird.

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

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

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

rekursive Kopie (die akzeptierte "Antwort")

Lassen Sie uns die Antwort akzeptiert einen Blick.

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, heh? Es ist eine rekursive Kopie des Objekts und Griffe andere Typen als auch, wie Date, aber das war keine Anforderung.

var b = cloneSO(a);

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

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

nativen Lösung

Nach einem längeren Diskussion mit meinem Kollegen, mein Chef fragte uns, was passiert ist, und er fand eine einfache Lösung nach einigen googeln. Es heißt Object.create.

var b = Object.create(a);

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

Diese Lösung wurde auf Javascript hinzugefügt vor einiger Zeit und meistert 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, es hat nicht innerhalb der verschachtelten Struktur arbeiten.

polyfill für die native Lösung

Es gibt eine polyfill für Object.create im älteren Browser wie der Internet Explorer 8. Es ist so etwas wie von Mozilla empfohlen, und natürlich ist es nicht perfekt und die Ergebnisse in das gleiche Problem wie die nativen 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 nicht in den Anwendungsbereich setzen F so können wir einen Blick auf das, 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

Die gleiche Problem wie die nativen Lösung , aber ein wenig schlechter Ausgang.

, desto besser (aber nicht perfekt) Lösung

Wenn um zu graben, fand ich eine ähnliche Frage ( In Javascript, wenn eine tiefe Kopie durchführen, wie vermeide ich einen Zyklus aufgrund einer Eigenschaft ist "die"? ) zu diesem, aber mit einer Art und Weise bessere 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';

Und lassen Sie sie am Ausgang einen Blick ...

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 abgestimmt sind, aber es gibt noch einige kleinere Probleme, einschließlich der instance von nested verändern und circ Object.

  

Die Struktur von Bäumen, die ein Blatt teilen nicht kopiert werden, werden sie zwei unabhängige Blätter werden:

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

Schlussfolgerung

Die letzte Lösung Rekursion und einen Cache verwendet, kann nicht die beste sein, aber es ist eine real Tief Kopie des Objekts. Es behandelt einfache properties, circular structures und nested object, aber es wird die Instanz von ihnen versauen, während das Klonen.

jsfiddle

Wenn Sie mit einem flachen Kopie in Ordnung sind, dann hat die Underscore.js Bibliothek ein Methode klonen .

y = _.clone(x);

oder Sie können es wie

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

OK vorstellen, haben Sie dieses Objekt unten und Sie wollen es klonen:

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

oder

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

Die Antwort ist vor allem depeneds auf denen ECMAscript Sie verwenden, in ES6+, können Sie einfach Object.assign verwenden Sie den Klon zu tun:

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

oder Spread-Operator wie folgt aus:

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

Aber wenn Sie ES5 verwenden, können Sie einige Methoden verwenden, aber die JSON.stringify, so stellen Sie sicher, dass Sie nicht für einen großen Teil der Daten mit kopieren, aber es könnte eine Linie praktische Möglichkeit, in vielen Fällen, so etwas wie diese:

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

Eine besonders unelegant Lösung ist JSON-Codierung zu verwenden, indem tiefe Kopien von Objekten zu machen, die nicht Mitglied Methoden. Die Methodik ist zu JSON kodieren Ihr Zielobjekt, dann durch Decodierung, erhalten Sie die Kopie die Sie suchen. Sie können so oft entschlüsseln, wie Sie so viele Kopien machen möchten, wie Sie benötigen.

Natürlich Funktionen gehören nicht in JSON, so funktioniert dies nur für Objekte ohne Mitglied Methoden.

Diese Methode war perfekt für meinen Anwendungsfall, da ich JSON Blobs in einem Schlüssel-Wert-Speicher bin speichern, und wenn sie als Objekte in einem JavaScript-API ausgesetzt sind, jedes Objekt enthält tatsächlich eine Kopie des ursprünglichen Zustandes der Objekt so können wir das Delta berechnen, nachdem der Anrufer den exponierten Objekt mutiert ist.

var object1 = {key:"value"};
var object2 = object1;

object2 = JSON.stringify(object1);
object2 = JSON.parse(object2);

object2.key = "a change";
console.log(object1);// returns value

Sie können einfach eine zu verbreiten Eigentum ein Objekt ohne Referenzen kopieren. Aber Vorsicht (siehe Kommentare), die ‚Kopie‘ ist nur auf der untersten Objekt / Array-Ebene. Verschachtelte Objekte sind noch Referenzen!


Komplette Klon:

let x = {a: 'value1'}
let x2 = {...x}

// => mutate without references:

x2.a = 'value2'
console.log(x.a)    // => 'value1'

Klonen mit Referenzen auf der zweiten Ebene:

const y = {a: {b: 'value3'}}
const y2 = {...y}

// => nested object is still a references:

y2.a.b = 'value4'
console.log(y.a.b)    // => 'value4'

JavaScript unterstützt eigentlich nicht tief Klone nativ. Verwenden Sie eine Nutzenfunktion. Zum Beispiel Ramda:

  

http://ramdajs.com/docs/#clone

Für jene AngularJS verwenden, gibt es auch eine direkte Methode zur Klonierung oder Erweiterung der Objekte in dieser Bibliothek.

var destination = angular.copy(source);

oder

angular.copy(source, destination);

Mehr in angular.copy Dokumentation ...

A.Levy Antwort ist fast abgeschlossen, hier ist mein kleiner Beitrag: gibt es einen Weg, wie rekursive Referenzen behandeln finden Sie in dieser Zeile

if(this[attr]==this) copy[attr] = copy;

Wenn das Objekt XML-DOM-Element ist, müssen wir verwenden cloneNode statt

if(this.cloneNode) return this.cloneNode(true);

Inspiriert von A.Levy der umfassenden Studie und Calvins Prototyping Ansatz biete ich diese Lösung:

Object.prototype.clone = function() {
  if(this.cloneNode) return this.cloneNode(true);
  var copy = this instanceof Array ? [] : {};
  for(var attr in this) {
    if(typeof this[attr] == "function" || this[attr]==null || !this[attr].clone)
      copy[attr] = this[attr];
    else if(this[attr]==this) copy[attr] = copy;
    else copy[attr] = this[attr].clone();
  }
  return copy;
}

Date.prototype.clone = function() {
  var copy = new Date();
  copy.setTime(this.getTime());
  return copy;
}

Number.prototype.clone = 
Boolean.prototype.clone =
String.prototype.clone = function() {
  return this;
}

Siehe auch Anmerkung Andy Burke in den Antworten.

Hier ist eine Funktion, die Sie verwenden können.

function clone(obj) {
    if(obj == null || typeof(obj) != 'object')
        return obj;    
    var temp = new obj.constructor(); 
    for(var key in obj)
        temp[key] = clone(obj[key]);    
    return temp;
}

Aus diesen Artikel: Wie Arrays und Objekte in Javascript kopieren von Brian Huisman:

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

In ES-6 können Sie einfach Object.assign (...). Ex:

let obj = {person: 'Thor Odinson'};
let clone = Object.assign({}, obj);

Eine gute Referenz ist hier: https://googlechrome.github.io/samples/object-assign-es6/

In ECMAScript 2018

let objClone = { ...obj };

Beachten Sie, dass verschachtelte Objekte werden noch kopiert Als Referenz.

Sie können ein Objekt klonen und jeden Hinweis von der vorherigen entfernen, um eine einzige Zeile Code. Einfach tun:

var obj1 = { text: 'moo1' };
var obj2 = Object.create(obj1); // Creates a new clone without references

obj2.text = 'moo2'; // Only updates obj2's text property

console.log(obj1, obj2); // Outputs: obj1: {text:'moo1'}, obj2: {text:'moo2'}

Für Browser / Motoren, die derzeit nicht unterstützen Object.create Sie dieses polyfill verwenden können:

// Polyfill Object.create if it does not exist
if (!Object.create) {
    Object.create = function (o) {
        var F = function () {};
        F.prototype = o;
        return new F();
    };
}

Neue Antwort auf eine alte Frage! Wenn Sie das Vergnügen haben, mit ECMAScript mit 2016 (ES6) mit Spread-Syntax , ist es einfach.

keepMeTheSame = {first: "Me!", second: "You!"};
cloned = {...keepMeTheSame}

Dies stellt eine saubere Methode für eine flache Kopie eines Objekts. Einen tiefen Kopie, was bedeutet, eine neue Kopie von jedem Wert in jedem rekursiv verschachtelten Objekt makign, erfordert auf den schwereren Lösungen oben.

JavaScript hält weiter entwickelt.

let clone = Object.assign( Object.create( Object.getPrototypeOf(obj)), obj)

ES6 Lösung, wenn Sie wollen (flache) klonen eine Klasseninstanz und nicht nur eine Eigenschaft Objekt.

Sie interessieren sich für das Klonen einfache Objekte:

JSON.parse(JSON.stringify(json_original));

Quelle: JavaScript-Objekt neue Variable nicht durch Verweis?

kopieren

Mit Lodash:

var y = _.clone(x, true);

Ich denke, es ist eine einfache und Arbeits Antwort. In tiefem Kopieren gibt es zwei Anliegen:

  1. Halten Eigenschaften unabhängig voneinander.
  2. und halten Sie die Methoden lebendig auf geklonte Objekt.

Also ich denke, eine einfache Lösung, um ersten serialize sein und deserialisieren und dann ein Assign tut auf sie zu Funktionen zu kopieren.

let deepCloned = JSON.parse(JSON.stringify(source));
let merged = Object.assign({}, source);
Object.assign(merged, deepCloned);

Obwohl diese Frage viele Antworten hat, ich hoffe, dass dies ein zu können.

Für eine weitere Kopie und Klon, JSON.stringify dann JSON.parse das Objekt:

obj = { a: 0 , b: { c: 0}};
let deepClone = JSON.parse(JSON.stringify(obj));
obj.a = 5;
obj.b.c = 5;
console.log(JSON.stringify(deepClone)); // { a: 0, b: { c: 0}}

Ich wollte nur alle Object.create Lösungen in diesem Beitrag hinzufügen, dass dies nicht mit NodeJS in der gewünschten Weise funktioniert.

In Firefox das Ergebnis

var a = {"test":"test"};
var b = Object.create(a);
console.log(b);´

ist

{test:"test"}.

In NodeJS es ist

{}

Dies ist eine Adaption von A. Levy Code auch das Klonen von Funktionen und mehrere / zyklischen Referenzen zu behandeln - was bedeutet, dass, wenn zwei Objekte im Baum, die Referenzen des gleichen Objekts sind geklont wird, Objektbaum geklont werden diese Eigenschaften auf ein und demselben Klon des referenzierten Objekts zeigen. Dies löst auch den Fall von zyklischen Abhängigkeiten, die, wenn links nicht behandelte, zu einer Endlosschleife führt. Die Komplexität des Algorithmus ist O (n)

function clone(obj){
    var clonedObjectsArray = [];
    var originalObjectsArray = []; //used to remove the unique ids when finished
    var next_objid = 0;

    function objectId(obj) {
        if (obj == null) return null;
        if (obj.__obj_id == undefined){
            obj.__obj_id = next_objid++;
            originalObjectsArray[obj.__obj_id] = obj;
        }
        return obj.__obj_id;
    }

    function cloneRecursive(obj) {
        if (null == obj || typeof obj == "string" || typeof obj == "number" || typeof obj == "boolean") 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; i < obj.length; ++i) {
                copy[i] = cloneRecursive(obj[i]);
            }
            return copy;
        }

        // Handle Object
        if (obj instanceof Object) {
            if (clonedObjectsArray[objectId(obj)] != undefined)
                return clonedObjectsArray[objectId(obj)];

            var copy;
            if (obj instanceof Function)//Handle Function
                copy = function(){return obj.apply(this, arguments);};
            else
                copy = {};

            clonedObjectsArray[objectId(obj)] = copy;

            for (var attr in obj)
                if (attr != "__obj_id" && obj.hasOwnProperty(attr))
                    copy[attr] = cloneRecursive(obj[attr]);                 

            return copy;
        }       


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



    //remove the unique ids
    for (var i = 0; i < originalObjectsArray.length; i++)
    {
        delete originalObjectsArray[i].__obj_id;
    };

    return cloneObj;
}

Einige schnelle Tests

var auxobj = {
    prop1 : "prop1 aux val", 
    prop2 : ["prop2 item1", "prop2 item2"]
    };

var obj = new Object();
obj.prop1 = "prop1_value";
obj.prop2 = [auxobj, auxobj, "some extra val", undefined];
obj.nr = 3465;
obj.bool = true;

obj.f1 = function (){
    this.prop1 = "prop1 val changed by f1";
};

objclone = clone(obj);

//some tests i've made
console.log("test number, boolean and string cloning: " + (objclone.prop1 == obj.prop1 && objclone.nr == obj.nr && objclone.bool == obj.bool));

objclone.f1();
console.log("test function cloning 1: " + (objclone.prop1 == 'prop1 val changed by f1'));
objclone.f1.prop = 'some prop';
console.log("test function cloning 2: " + (obj.f1.prop == undefined));

objclone.prop2[0].prop1 = "prop1 aux val NEW";
console.log("test multiple references cloning 1: " + (objclone.prop2[1].prop1 == objclone.prop2[0].prop1));
console.log("test multiple references cloning 2: " + (objclone.prop2[1].prop1 != obj.prop2[0].prop1));
function clone(src, deep) {

    var toString = Object.prototype.toString;
    if(!src && typeof src != "object"){
        //any non-object ( Boolean, String, Number ), null, undefined, NaN
        return src;
    }

    //Honor native/custom clone methods
    if(src.clone && toString.call(src.clone) == "[object Function]"){
        return src.clone(deep);
    }

    //DOM Elements
    if(src.nodeType && toString.call(src.cloneNode) == "[object Function]"){
        return src.cloneNode(deep);
    }

    //Date
    if(toString.call(src) == "[object Date]"){
        return new Date(src.getTime());
    }

    //RegExp
    if(toString.call(src) == "[object RegExp]"){
        return new RegExp(src);
    }

    //Function
    if(toString.call(src) == "[object Function]"){
        //Wrap in another method to make sure == is not true;
        //Note: Huge performance issue due to closures, comment this :)
        return (function(){
            src.apply(this, arguments);
        });

    }

    var ret, index;
    //Array
    if(toString.call(src) == "[object Array]"){
        //[].slice(0) would soft clone
        ret = src.slice();
        if(deep){
            index = ret.length;
            while(index--){
                ret[index] = clone(ret[index], true);
            }
        }
    }
    //Object
    else {
        ret = src.constructor ? new src.constructor() : {};
        for (var prop in src) {
            ret[prop] = deep
                ? clone(src[prop], true)
                : src[prop];
        }
    }

    return ret;
};

Ich habe meine eigene Implementierung geschrieben. Nicht sicher, ob es zählt als eine bessere Lösung:

/*
    a function for deep cloning objects that contains other nested objects and circular structures.
    objects are stored in a 3D array, according to their length (number of properties) and their depth in the original object.
                                    index (z)
                                         |
                                         |
                                         |
                                         |
                                         |
                                         |                      depth (x)
                                         |_ _ _ _ _ _ _ _ _ _ _ _
                                        /_/_/_/_/_/_/_/_/_/
                                       /_/_/_/_/_/_/_/_/_/
                                      /_/_/_/_/_/_/...../
                                     /................./
                                    /.....            /
                                   /                 /
                                  /------------------
            object length (y)    /
*/

Im Anschluss an die Umsetzung:

function deepClone(obj) {
    var depth = -1;
    var arr = [];
    return clone(obj, arr, depth);
}

/**
 *
 * @param obj source object
 * @param arr 3D array to store the references to objects
 * @param depth depth of the current object relative to the passed 'obj'
 * @returns {*}
 */
function clone(obj, arr, depth){
    if (typeof obj !== "object") {
        return obj;
    }

    var length = Object.keys(obj).length; // native method to get the number of properties in 'obj'

    var result = Object.create(Object.getPrototypeOf(obj)); // inherit the prototype of the original object
    if(result instanceof Array){
        result.length = length;
    }

    depth++; // depth is increased because we entered an object here

    arr[depth] = []; // this is the x-axis, each index here is the depth
    arr[depth][length] = []; // this is the y-axis, each index is the length of the object (aka number of props)
    // start the depth at current and go down, cyclic structures won't form on depths more than the current one
    for(var x = depth; x >= 0; x--){
        // loop only if the array at this depth and length already have elements
        if(arr[x][length]){
            for(var index = 0; index < arr[x][length].length; index++){
                if(obj === arr[x][length][index]){
                    return obj;
                }
            }
        }
    }

    arr[depth][length].push(obj); // store the object in the array at the current depth and length
    for (var prop in obj) {
        if (obj.hasOwnProperty(prop)) result[prop] = clone(obj[prop], arr, depth);
    }

    return result;
}

Jan Turoň Antwort oben ist sehr nah, und kann die beste sein, in einem Browser zu verwenden, aufgrund von Kompatibilitätsproblemen, aber es wird möglicherweise einige seltsame Aufzählung Probleme verursachen. Zum Beispiel ausführen:

for ( var i in someArray ) { ... }

wird der Klon () Methode i zuzuweisen, nachdem durch die Elemente des Arrays iteriert. Hier ist eine Anpassung, die die Aufzählung und arbeitet mit node.js vermeidet:

Object.defineProperty( Object.prototype, "clone", {
    value: function() {
        if ( this.cloneNode )
        {
            return this.cloneNode( true );
        }

        var copy = this instanceof Array ? [] : {};
        for( var attr in this )
        {
            if ( typeof this[ attr ] == "function" || this[ attr ] == null || !this[ attr ].clone )
            {
                copy[ attr ] = this[ attr ];
            }
            else if ( this[ attr ] == this )
            {
                copy[ attr ] = copy;
            }
            else
            {
                copy[ attr ] = this[ attr ].clone();
            }
        }
        return copy;
    }
});

Object.defineProperty( Date.prototype, "clone", {
    value: function() {
        var copy = new Date();
        copy.setTime( this.getTime() );
        return copy;
    }
});

Object.defineProperty( Number.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( Boolean.prototype, "clone", { value: function() { return this; } } );
Object.defineProperty( String.prototype, "clone", { value: function() { return this; } } );

Dies vermeidet die Methode clone () enumerable weil DefineProperty () defaults enumerable auf false zu machen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top