it-swarm.com.de

Wie klone ich ein JavaScript-Objekt richtig?

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

Wie klone ich ein JavaScript-Objekt richtig?

2859
mindeavor

Dies für ein Objekt in JavaScript zu tun, ist nicht einfach oder unkompliziert. Sie haben das Problem, dass Sie fälschlicherweise Attribute aus dem Prototyp des Objekts abrufen, die im Prototyp verbleiben und nicht in die neue Instanz kopiert werden sollen. Wenn Sie beispielsweise Object.prototype eine clone -Methode hinzufügen, müssen Sie dieses Attribut, wie einige Antworten zeigen, explizit überspringen. Aber was ist, wenn zu Object.prototype andere zusätzliche Methoden hinzugefügt werden oder wenn andere Zwischenprototypen vorhanden sind, von denen Sie nichts wissen? In diesem Fall kopieren Sie Attribute, die Sie nicht sollten. Daher müssen Sie unvorhergesehene, nicht lokale Attribute mit der Methode hasOwnProperty erkennen.

Zusätzlich zu nicht aufzählbaren Attributen tritt beim Kopieren von Objekten mit ausgeblendeten Eigenschaften ein schwierigeres Problem auf. Beispielsweise ist prototype eine verborgene Eigenschaft einer Funktion. Außerdem wird auf den Prototyp eines Objekts mit dem Attribut __proto__ verwiesen, das ebenfalls ausgeblendet ist und nicht von einer for/in-Schleife kopiert wird, die über die Attribute des Quellobjekts iteriert. Ich denke, __proto__ ist möglicherweise spezifisch für den JavaScript-Interpreter von Firefox und in anderen Browsern möglicherweise etwas anderes, aber Sie sehen das Bild. Nicht alles ist aufzählbar. Sie können ein verstecktes Attribut kopieren, wenn Sie seinen Namen kennen, aber ich kenne keine Möglichkeit, es automatisch zu erkennen.

Ein weiterer Haken bei der Suche nach einer eleganten Lösung ist das Problem, die Vererbung des Prototyps korrekt einzurichten. Wenn der Prototyp Ihres Quellobjekts Object ist, funktioniert es, einfach ein neues allgemeines Objekt mit {} zu erstellen. Wenn der Prototyp der Quelle jedoch ein Nachkomme von Object ist, werden Ihnen die zusätzlichen Mitglieder dieses Prototyps fehlen, den Sie haben Übersprungen mit dem hasOwnProperty-Filter oder die im Prototyp waren, aber überhaupt nicht aufzählbar waren. Eine Lösung könnte darin bestehen, die constructor -Eigenschaft des Quellobjekts aufzurufen, um das ursprüngliche Kopierobjekt abzurufen und dann die Attribute zu kopieren. In diesem Fall erhalten Sie jedoch keine nicht aufzählbaren Attribute. Beispielsweise speichert ein Date -Objekt seine Daten als verborgenes Element:

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

Die Datumszeichenfolge für d1 liegt 5 Sekunden hinter der von d2. Eine Möglichkeit, eine Date-Klasse mit einer anderen identisch zu machen, besteht darin, die setTime-Methode aufzurufen, die jedoch für die Date-Klasse spezifisch ist. Ich glaube nicht, dass es eine kugelsichere allgemeine Lösung für dieses Problem gibt, obwohl ich mich freuen würde, wenn ich mich irre!

Als ich allgemein tiefes Kopieren implementieren musste, ging ich davon aus, dass ich nur ein einfaches Object, Array, Date, String, Number oder Boolean kopieren musste. Die letzten 3 Typen sind unveränderlich, so dass ich eine flache Kopie erstellen und mich nicht darum kümmern könnte, ob sich etwas ändert. Ich nahm weiter an, dass alle Elemente, die in Object oder Array enthalten sind, auch einer der 6 einfachen Typen in dieser Liste sind. Dies kann mit folgendem Code 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 für die 6 einfachen Typen, die ich erwähnt habe, angemessen funktionieren, solange die Daten in den Objekten und Arrays eine Baumstruktur bilden. Das heißt, das Objekt enthält nicht mehr als einen Verweis auf dieselben Daten. 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 kein JavaScript-Objekt verarbeiten können, aber es kann für viele Zwecke ausreichen, solange Sie nicht davon ausgehen, dass es nur für alles funktioniert, was Sie darauf werfen.

1471
A. Levy

Wenn Sie in Ihrem Objekt nicht Dates, functions, undefined oder Infinity verwenden, ist JSON.parse(JSON.stringify(object)) ein sehr einfacher Einzeiler:

const a = {
  string: 'string',
  number: 123,
  bool: false,
  nul: null,
  date: new Date(),  // stringified
  undef: undefined,  // lost
  inf: Infinity,  // forced to 'null'
}
console.log(a);
console.log(typeof a.date);  // Date object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
console.log(typeof clone.date);  // result of .toISOString()

Dies funktioniert für alle Arten von Objekten, die Objekte, Arrays, Zeichenfolgen, Boolesche Werte und Zahlen enthalten.

Siehe auch dieser Artikel über den strukturierten Klon-Algorithmus von Browsern , der beim Posten von Nachrichten an und von einem Worker verwendet wird. Es enthält auch eine Funktion zum tiefen Klonen.

894
heinob

Mit jQuery können Sie flache Kopien mit extend erstellen:

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

nachfolgende Änderungen an copiedObject wirken sich nicht auf originalObject aus und umgekehrt.

Oder um eine tiefe Kopie zu erstellen :

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

In ECMAScript 6 gibt es die Methode Object.assign , mit der Werte aller aufzählbaren eigenen Eigenschaften von einem Objekt in ein anderes kopiert werden. Zum Beispiel:

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

Beachten Sie jedoch, dass verschachtelte Objekte weiterhin als Referenz kopiert werden.

625

Per MDN :

  • Wenn Sie eine flache Kopie wünschen, verwenden Sie Object.assign({}, a)
  • Verwenden Sie für "tiefes" Kopieren JSON.parse(JSON.stringify(a))

Externe Bibliotheken sind nicht erforderlich, Sie müssen jedoch Browserkompatibilität zuerst überprüfen.

191
Tareq

Es gibt viele Antworten, aber keine, die Object.create aus ECMAScript 5 erwähnen, das Ihnen zugegebenermaßen keine exakte Kopie liefert, sondern die Quelle als Prototyp des neuen Objekts festlegt.

Dies ist also keine exakte Antwort auf die Frage, sondern eine einzeilige und somit elegante Lösung. Und es funktioniert am besten für 2 Fälle:

  1. Wo ein solches Erbe nützlich ist (duh!)
  2. Wenn das Quellobjekt nicht geändert wird, ist die Beziehung zwischen den beiden Objekten kein Problem.

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 für überlegen? Es ist native, also keine Schleife, keine Rekursion. Ältere Browser benötigen jedoch eine Polyfill.

128
itpastorn

Eine elegante Möglichkeit, ein Javascript-Objekt in eine Codezeile zu klonen

Eine Object.assign -Methode ist Teil des ECMAScript 2015 (ES6) -Standards und bietet genau das, was Sie benötigen.

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

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

Weiterlesen ...

Die Polyfüllung zur Unterstützung älterer Browser:

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;
    }
  });
}
119
Eugene Tiurin

Bei den meisten Lösungen im Internet gibt es mehrere Probleme. Also habe ich beschlossen, eine Folgeuntersuchung durchzuführen, die auch beinhaltet, warum die akzeptierte Antwort nicht akzeptiert werden sollte.

ausgangssituation

Ich möchte ein Javascript Object mit all seinen Kindern und ihren Kindern und so weiter tief kopieren . Aber da ich kein normaler Entwickler bin, hat mein Object normales properties, circular structures und sogar nested objects .

Erstellen wir also zuerst einen circular structure und einen nested object.

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

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

Lassen Sie uns alles in einem Object mit dem Namen a zusammenführen.

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

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

var b = a;

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

Sie wissen, was hier passiert ist, denn wenn nicht, würden Sie nicht einmal auf diese großartige Frage stoß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"
    }
}

Nun wollen 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';

Verschwende nicht zu viel Zeit, du bekommst 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, was? Es ist eine rekursive Kopie des Objekts und behandelt auch andere Typen wie Date, aber das war keine Voraussetzung.

var b = cloneSO(a);

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

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

native Lösung

Nachdem mein Chef mit meinem Kollegen gestritten hatte, fragte er uns, was passiert sei und fand nach einigem googeln eine einfache Lösung . 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, es hat nicht funktioniert mit der verschachtelten Struktur im Inneren.

polyfill für die native Lösung

Es gibt eine Polyfüllung für Object.create im älteren Browser, genau wie die IE 8. Sie wird von Mozilla empfohlen, und natürlich ist sie nicht perfekt und führt zum gleichen Problem wie die 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 F außerhalb des Bereichs gesetzt, damit wir einen Blick darauf werfen können, was instanceof uns sagt.

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

Gleiches Problem wie die native Lösung , aber etwas schlechtere Ausgabe.

die bessere (aber nicht perfekte) Lösung

Beim Stöbern fand ich eine ähnliche Frage ( In Javascript, wenn ich eine tiefe Kopie durchführe, wie vermeide ich einen Zyklus, weil eine Eigenschaft "dies" ist? ) zu dieser, aber mit einem viel 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 schauen wir uns die Ausgabe an ...

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 stimmen überein, es gibt jedoch noch einige kleinere Probleme, darunter das Ändern von instance von nested und circ in 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]

fazit

Die letzte Lösung, die Rekursion und einen Cache verwendet, ist möglicherweise nicht die beste, aber es ist eine echte Kopie des Objekts. Es behandelt einfache properties, circular structures und nested object, aber es wird die Instanz von ihnen während des Klonens durcheinander bringen.

jsfiddle

81
Fabio Poloni

Wenn Sie mit einer flachen Kopie einverstanden sind, verfügt die Bibliothek unterstrich.js über eine Klon -Methode.

y = _.clone(x);

oder du kannst es gerne erweitern

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

OK, Stellen Sie sich vor, Sie haben dieses Objekt unten und möchten es klonen:

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

oder

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

die Antwort hängt hauptsächlich davon ab, von wem ECMAscript Sie in ES6+ verwenden. Sie können Object.assign einfach verwenden, um den Klon durchzuführen:

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

oder mit Spread Operator wie folgt:

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

Wenn Sie jedoch ES5 verwenden, können Sie nur wenige Methoden verwenden, aber den JSON.stringify. Achten Sie nur darauf, dass Sie nicht zu viele Daten kopieren, aber in vielen Fällen kann dies eine Zeile sein , etwas wie das:

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
51
Alireza

Eine besonders unelegante Lösung ist die Verwendung der JSON-Codierung, um tiefe Kopien von Objekten ohne Member-Methoden zu erstellen. Die Methode besteht darin, Ihr Zielobjekt mit JSON zu kodieren. Durch Dekodieren erhalten Sie dann die gesuchte Kopie. Sie können so oft dekodieren, wie Sie möchten, um so viele Kopien wie nötig zu erstellen.

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

Diese Methode war perfekt für meinen Anwendungsfall, da ich JSON-Blobs in einem Schlüsselwertspeicher speichere und wenn sie als Objekte in einer JavaScript-API verfügbar gemacht werden, enthält jedes Objekt tatsächlich eine Kopie des ursprünglichen Zustands des Objekts kann das Delta berechnen, nachdem der Anrufer das exponierte Objekt mutiert hat.

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

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

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

Sie können einfach ein Spread-Eigenschaft verwenden, um ein Objekt ohne Referenzen zu kopieren. Aber seien Sie vorsichtig (siehe Kommentare), die 'Kopie' befindet sich nur auf der untersten Objekt-/Array-Ebene. Verschachtelte Objekte sind immer noch Referenzen!


Vollständiger 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 Deep Clones eigentlich nicht. Verwenden Sie eine Utility-Funktion. Zum Beispiel Ramda:

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

34
musemind

Für Benutzer von AngularJS gibt es auch direkte Methoden zum Klonen oder Erweitern der Objekte in dieser Bibliothek.

var destination = angular.copy(source);

oder

angular.copy(source, destination);

Mehr in angle.copy Dokumentation ...

25
Lukas Jelinek

A.Levys Antwort ist fast vollständig. Hier ist mein kleiner Beitrag: es gibt eine Möglichkeit, mit rekursiven Referenzen umzugehen, siehe diese Zeile

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

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

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

Inspiriert von A.Levys umfassender Studie und dem Prototyping-Ansatz von Calvin, biete ich diese Lösung an:

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 den Hinweis von Andy Burke in den Antworten.

22
Jan Turoň

Aus diesem Artikel: Kopieren von Arrays und Objekten in Javascript 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;
};
19
Calvin

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

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

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

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

18
João Oliveira

In ECMAScript 2018

let objClone = { ...obj };

Beachten Sie, dass verschachtelte Objekte noch kopiert werden als Referenz

17
Pavan Garre

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

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

Dies bietet eine saubere Methode für eine flache Kopie eines Objekts. Das Erstellen einer tiefen Kopie, dh das Erstellen einer neuen Kopie jedes Werts in jedem rekursiv verschachtelten Objekt, erfordert eine der schwereren oben genannten Lösungen.

JavaScript entwickelt sich weiter.

13
Charles Merriam

Sie können ein Objekt mit einer einzigen Codezeile klonen und alle Verweise aus dem vorherigen entfernen. Mach einfach:

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/Engines, die Object.create derzeit nicht unterstützen, können Sie diese Polyfill verwenden:

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

Sie möchten einfache Objekte klonen:

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

Quelle: Wie kopiere ich ein JavaScript-Objekt NICHT per Referenz in eine neue Variable?

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

ES6-Lösung, wenn Sie ein Klasseninstanz und nicht nur ein Eigenschaftsobjekt (flach) klonen möchten.

11
flori

Verwendung von Lodash:

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

Ich denke, es gibt eine einfache und funktionierende Antwort. Beim tiefen Kopieren gibt es zwei Bedenken:

  1. Halten Sie die Eigenschaften unabhängig voneinander.
  2. Und halten Sie die Methoden auf dem geklonten Objekt am Leben.

Daher denke ich, dass eine einfache Lösung darin bestehen wird, zuerst die Funktionen zu serialisieren und zu deserialisieren und sie dann zuzuweisen, um auch die 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, hoffe ich, dass diese auch hilft.

9
ConductedClever

Für eine tiefe Kopie und einen 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}}
7
Nishant Dwivedi
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;
};
6
user1547016

Dies ist eine Anpassung von A. Levys Code, um auch das Klonen von Funktionen und mehrfachen/zyklischen Referenzen zu handhaben. Dies bedeutet, dass wenn zwei Eigenschaften in dem Baum, der geklont wird, Referenzen desselben Objekts sind, der geklonte Objektbaum diese haben wird Eigenschaften verweisen auf ein und denselben Klon des referenzierten Objekts. Dies löst auch den Fall von zyklischen Abhängigkeiten, die, wenn sie nicht behandelt werden, zu einer Endlosschleife führen. 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));
6
Radu Simionescu

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

In Firefox ist das Ergebnis von

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

ist

{test:"test"}.

In nodejs ist es

{}
6
heinob

Ich habe meine eigene Implementierung geschrieben. Ich bin mir nicht sicher, ob es eine bessere Lösung ist:

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

Es folgt die Implementierung:

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

(Das Folgende war hauptsächlich eine Integration von @ Maciej Bukowski , @ A. Levy , @ Jan Turoň , @ Red = 's Antworten und @ LeviRoberts , @ RobG ' s Kommentare, vielen Dank an sie !!!)

Deep Copy ? - JA! (meist);
Flache Kopie ? - NEIN! (außer Proxy).

Ich heiße alle herzlich willkommen, clone() zu testen.
Darüber hinaus kann mit defineProp() jede Art von Deskriptor einfach und schnell (neu) definiert oder kopiert werden.

Funktion

function clone(object) {
  /*
    Deep copy objects by value rather than by reference,
    exception: `Proxy`
  */

  const seen = new WeakMap()

  return (function clone(object) {
    if (object !== Object(object)) return object /*
    —— Check if the object belongs to a primitive data type */

    if (object instanceof Node) return object.cloneNode(true) /*
    —— Clone DOM trees */

    let _object // The clone of object

    switch (object.constructor) {
      case Object:
      case Array:
        _object = cloneObject(object)
        break

      case Date:
        _object = new Date(+object)
        break

      case Function:
        const fnStr = String(object)

        _object = new Function("return " +
          (/^(?!function |[^{]+?=>)[^(]+?\(/.test(fnStr)
            ? "function " : ""
          ) + fnStr
        )()

        Object.defineProperties(_object,
          Object.getOwnPropertyDescriptors(object)
        )
        break

      default:
        switch (Object.prototype.toString.call(object.constructor)) {
          //                              // Stem from:
          case "[object Function]":       // `class`
          case "[object Undefined]":      // `Object.create(null)`
            _object = cloneObject(object)
            break

          default:                        // `Proxy`
            _object = object
        }
    }

    return _object


    function cloneObject(object) {
      if (seen.has(object)) return seen.get(object) /*
      —— Handle recursive references (circular structures) */

      const _object = Array.isArray(object)
        ? []
        : Object.create(Object.getPrototypeOf(object)) /*
          —— Assign [[Prototype]] for inheritance */

      seen.set(object, _object) /*
      —— Make `_object` the associative mirror of `object` */

      Reflect.ownKeys(object).forEach(key =>
        defineProp(_object, key, { value: clone(object[key]) }, object)
      )

      return _object
    }
  })(object)
}


function defineProp(object, key, descriptor = {}, copyFrom = {}) {
  const prevDesc = Object.getOwnPropertyDescriptor(object, key)
    || { configurable: true, writable: true }
    , copyDesc = Object.getOwnPropertyDescriptor(copyFrom, key)
      || { configurable: true, writable: true } // Custom…
      || {} // …or left to native default settings

  const { configurable: _configurable, writable: _writable } = prevDesc
    , test = _writable === undefined
      ? _configurable // Can redefine property
      : _configurable && _writable // Can assign to property

  if (!test || arguments.length <= 2) return test;

  ["get", "set", "value", "writable", "enumerable", "configurable"]
    .forEach(k =>
      descriptor[k] === undefined && (descriptor[k] = copyDesc[k])
    )

  const { get, set, value, writable, enumerable, configurable }
    = descriptor

  return Object.defineProperty(object, key, {
    enumerable, configurable, ...get || set
      ? { get, set } // Accessor descriptor
      : { value, writable } // Data descriptor
  })
}

// Tests

"use strict"
const obj0 = {

  u: undefined,

  nul: null,

  t: true,

  n: 9,

  str1: "string",

  str2: "",

  sym: Symbol("symbol"),

  [Symbol("e")]: Math.E,

  f: {
    getAccessorStr(object) {
      return []
        .concat(...
          Object.values(Object.getOwnPropertyDescriptors(object))
            .filter(desc => desc.writable === undefined)
            .map(desc => Object.values(desc))
        )
        .filter(prop => typeof prop === "function")
        .map(String)
    },
    f0: function f0() { },
    f1: function () { },
    f2: a => a / (a + 1),
    f3: () => 0,
    f4(params) { return param => param + params },
    f5: (a, b) => ({ c = 0 } = {}) => a + b + c
  },

  o: {
    n: 0,
    o: {
      f: function (...args) { }
    }
  },

  arr: [[0], [1, 2]],

  d: new Date(),

  get g() { return 0 }
}

defineProp(obj0, "s", {
  set(v) { this._s = v }
})
defineProp(obj0.arr, "tint", {
  value: { is: "non-enumerable" }
})
obj0.arr[0].name = "nested array"


let obj1 = clone(obj0)
obj1.o.n = 1
obj1.o.o.g = function g(a = 0, b = 0) { return a + b }
obj1.arr[1][1] = 3
obj1.d.setTime(+obj0.d + 60 * 1000)
obj1.arr.tint.is = "enumerable? no"
obj1.arr[0].name = "a nested arr"
defineProp(obj1, "s", {
  set(v) { this._s = v + 1 }
})

console.log("\n\n" + "-".repeat(2 ** 6))




console.log(">:>: Test - Routinely")

console.log("obj0:\n ", JSON.stringify(obj0))
console.log("obj1:\n ", JSON.stringify(obj1))
console.log()

console.log("obj0:\n ", obj0)
console.log("obj1:\n ", obj1)
console.log()

console.log("obj0\n ",
  ".arr.tint:", obj0.arr.tint, "\n ",
  ".arr[0].name:", obj0.arr[0].name
)
console.log("obj1\n ",
  ".arr.tint:", obj1.arr.tint, "\n ",
  ".arr[0].name:", obj1.arr[0].name
)
console.log()

console.log("Accessor-type descriptor\n ",
  "of obj0:", obj0.f.getAccessorStr(obj0), "\n ",
  "of obj1:", obj1.f.getAccessorStr(obj1), "\n ",
  "set (obj0 & obj1) .s :", obj0.s = obj1.s = 0, "\n ",
  "  → (obj0 , obj1) ._s:", obj0._s, ",", obj1._s
)

console.log("—— obj0 has not been interfered.")

console.log("\n\n" + "-".repeat(2 ** 6))




console.log(">:>: Test - Circular structures")

obj0.o.r = {}
obj0.o.r.recursion = obj0.o
obj0.arr[1] = obj0.arr

obj1 = clone(obj0)
console.log("obj0:\n ", obj0)
console.log("obj1:\n ", obj1)

console.log("Clear obj0's recursion:",
  obj0.o.r.recursion = null, obj0.arr[1] = 1
)
console.log(
  "obj0\n ",
  ".o.r:", obj0.o.r, "\n ",
  ".arr:", obj0.arr
)
console.log(
  "obj1\n ",
  ".o.r:", obj1.o.r, "\n ",
  ".arr:", obj1.arr
)
console.log("—— obj1 has not been interfered.")


console.log("\n\n" + "-".repeat(2 ** 6))




console.log(">:>: Test - Classes")

class Person {
  constructor(name) {
    this.name = name
  }
}

class Boy extends Person { }
Boy.prototype.sex = "M"

const boy0 = new Boy
boy0.hobby = { sport: "spaceflight" }

const boy1 = clone(boy0)
boy1.hobby.sport = "superluminal flight"

boy0.name = "one"
boy1.name = "neo"

console.log("boy0:\n ", boy0)
console.log("boy1:\n ", boy1)
console.log("boy1's prototype === boy0's:",
  Object.getPrototypeOf(boy1) === Object.getPrototypeOf(boy0)
)

Verweise

  1. Object.create() | MDN
  2. Object.defineProperties() | MDN
  3. Aufzählung und Eigentum von Eigenschaften | MDN
  4. TypeError: zyklischer Objektwert | MDN

Sprachtricks verwendet

  1. Requisite bedingt zum Objekt hinzufügen
5
ooo

Die Antwort von Jan Turoň oben ist sehr knapp und kann aufgrund von Kompatibilitätsproblemen am besten in einem Browser verwendet werden, kann jedoch zu seltsamen Aufzählungsproblemen führen. Zum Beispiel:

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

Weist i die clone () -Methode zu, nachdem die Elemente des Arrays durchlaufen wurden. Hier ist eine Anpassung, die die Aufzählung vermeidet und mit node.js funktioniert:

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

Dadurch wird vermieden, dass die clone () -Methode enumerierbar wird, da defineProperty () standardmäßig false ist.

5
Andy Burke

Da mindeavour angegeben hat, dass das zu klonende Objekt ein 'wörtlich konstruiertes' Objekt ist, könnte eine Lösung darin bestehen, einfach zu generieren das Objekt mehrmals anstatt eine Instanz des Objekts zu klonen:

function createMyObject()
{
    var myObject =
    {
        ...
    };
    return myObject;
}

var myObjectInstance1 = createMyObject();
var myObjectInstance2 = createMyObject();
5
Bert Regelink

Konsultieren Sie http://www.w3.org/html/wg/drafts/html/master/infrastructure.html#safe-passing-of-structured-data für die "sichere Weitergabe strukturierter Daten" des W3C "Algorithmus, der von Browsern implementiert werden soll, um Daten zB an Web - Worker weiterzuleiten. Es hat jedoch einige Einschränkungen, da es keine Funktionen handhabt. Weitere Informationen finden Sie unter https://developer.mozilla.org/en-US/docs/DOM/The_structured_clone_algorithm , einschließlich eines alternativen Algorithmus in JS, der Sie auf dem Weg dorthin unterstützt.

4
user663031
4
Radu Simionescu

Verwenden Sie deepcopy aus npm. Funktioniert sowohl im Browser als auch in node als npm module...

https://www.npmjs.com/package/deepcopy

let a = deepcopy(b)

4
user3071643

Strukturiertes Klonen

Der HTML-Standard enthält einen internen strukturierten Klon-/Serialisierungsalgorithmus , der tiefe Klone von Objekten erstellen kann. Es ist nach wie vor auf bestimmte integrierte Typen beschränkt, unterstützt jedoch neben den wenigen von JSON unterstützten Typen auch Daten, RegExps, Maps, Sets, Blobs, Dateilisten, ImageDaten, spärliche Arrays, typisierte Arrays und wahrscheinlich weitere . Außerdem werden Referenzen in den geklonten Daten beibehalten, sodass zyklische und rekursive Strukturen unterstützt werden können, die zu Fehlern bei JSON führen würden.

Unterstützung in Node.js: Experimentell ????

Das v8 -Modul in Node.js ist derzeit (ab Node 11) macht die strukturierte Serialisierungs-API direkt verfügbar , diese Funktionalität ist jedoch weiterhin als "experimentell" gekennzeichnet Änderungen oder Löschungen in zukünftigen Versionen vorbehalten. Wenn Sie eine kompatible Version verwenden, ist das Klonen eines Objekts so einfach wie:

const v8 = require('v8');

const structuredClone = obj => {
  return v8.deserialize(v8.serialize(obj));
};

Direkte Unterstützung in Browsern: Vielleicht irgendwann? ????

Browser bieten derzeit keine direkte Schnittstelle für den strukturierten Klonalgorithmus, aber eine globale Funktion structuredClone() wurde in whatwg/html # 793 on GitHub erläutert. Wie derzeit vorgeschlagen, wäre die Verwendung für die meisten Zwecke so einfach wie:

const clone = structuredClone(original);

Sofern dies nicht im Lieferumfang enthalten ist, werden die strukturierten Klonimplementierungen der Browser nur indirekt verfügbar gemacht.

Asynchrone Problemumgehung: Verwendbar. ????

Der geringere Aufwand zum Erstellen eines strukturierten Klons mit vorhandenen APIs besteht darin, die Daten über einen Port eines MessageChannels zu senden. Der andere Port gibt ein Ereignis message mit einem strukturierten Klon des angehängten .data aus. Leider ist das Abhören dieser Ereignisse zwangsläufig asynchron, und die synchronen Alternativen sind weniger praktisch.

class StructuredCloner {
  constructor() {
    this.pendingClones_ = new Map();
    this.nextKey_ = 0;

    const channel = new MessageChannel();
    this.inPort_ = channel.port1;
    this.outPort_ = channel.port2;

    this.outPort_.onmessage = ({data: {key, value}}) => {
      const resolve = this.pendingClones_.get(key);
      resolve(value);
      this.pendingClones_.delete(key);
    };
    this.outPort_.start();
  }

  cloneAsync(value) {
    return new Promise(resolve => {
      const key = this.nextKey_++;
      this.pendingClones_.set(key, resolve);
      this.inPort_.postMessage({key, value});
    });
  }
}

const structuredCloneAsync = window.structuredCloneAsync =
    StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner);

Beispiel Verwendung:

const main = async () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = await structuredCloneAsync(original);

  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));

  console.log("Assertions complete.");
};

main();

Synchronous Workarounds: Schrecklich! ????

Es gibt keine guten Möglichkeiten, strukturierte Klone synchron zu erstellen. Hier sind ein paar unpraktische Hacks.

history.pushState() und history.replaceState() erstellen beide einen strukturierten Klon ihres ersten Arguments und weisen diesen Wert history.state zu. Sie können dies verwenden, um einen strukturierten Klon eines Objekts wie dieses zu erstellen:

const structuredClone = obj => {
  const oldState = history.state;
  history.replaceState(obj, null);
  const clonedObj = history.state;
  history.replaceState(oldState, null);
  return clonedObj;
};

Beispiel Verwendung:

'use strict';

const main = () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = structuredClone(original);
  
  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));
  
  console.log("Assertions complete.");
};

const structuredClone = obj => {
  const oldState = history.state;
  history.replaceState(obj, null);
  const clonedObj = history.state;
  history.replaceState(oldState, null);
  return clonedObj;
};

main();

Obwohl synchron, kann dies extrem langsam sein. Der gesamte mit der Manipulation des Browserverlaufs verbundene Overhead fällt an. Das wiederholte Aufrufen dieser Methode kann dazu führen, dass Chrome vorübergehend nicht mehr reagiert.

Der Konstruktor Notification erstellt einen strukturierten Klon der zugehörigen Daten. Es wird auch versucht, dem Benutzer eine Browser-Benachrichtigung anzuzeigen. Dies schlägt jedoch unbemerkt fehl, es sei denn, Sie haben die Berechtigung zur Benachrichtigung angefordert. Falls Sie die Berechtigung für andere Zwecke haben, schließen wir die von uns erstellte Benachrichtigung sofort.

const structuredClone = obj => {
  const n = new Notification('', {data: obj, silent: true});
  n.onshow = n.close.bind(n);
  return n.data;
};

Beispiel Verwendung:

'use strict';

const main = () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = structuredClone(original);
  
  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));
  
  console.log("Assertions complete.");
};

const structuredClone = obj => {
  const n = new Notification('', {data: obj, silent: true});
  n.close();
  return n.data;
};

main();
3
Jeremy Banks

Klonen Sie ein Objekt basierend auf einem template. Was tun Sie, wenn Sie keine exakte Kopie, aber die Robustheit einer zuverlässigen Klonoperation wünschen, aber nur Bits geklont oder sicherstellen möchten, dass Sie die Existenz oder das Format jedes Attributwerts steuern können? geklont?

Ich trage dies bei, weil es für uns nützlich ist und wir es geschaffen haben, weil wir etwas Ähnliches nicht finden konnten. Sie können damit ein Objekt klonen, das auf einem template -Objekt basiert, das angibt, welche Attribute des Objekts geklont werden sollen, und die Vorlage ermöglicht es Funktionen, diese Attribute in etwas anderes umzuwandeln, wenn sie auf dem nicht vorhanden sind Quellobjekt oder wie auch immer Sie den Klon behandeln möchten. Wenn es nicht nützlich ist, kann diese Antwort sicher jemand löschen.

   function isFunction(functionToCheck) {
       var getType = {};
       return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
   }

   function cloneObjectByTemplate(obj, tpl, cloneConstructor) {
       if (typeof cloneConstructor === "undefined") {
           cloneConstructor = false;
       }
       if (obj == null || typeof (obj) != 'object') return obj;

       //if we have an array, work through it's contents and apply the template to each item...
       if (Array.isArray(obj)) {
           var ret = [];
           for (var i = 0; i < obj.length; i++) {
               ret.Push(cloneObjectByTemplate(obj[i], tpl, cloneConstructor));
           }
           return ret;
       }

       //otherwise we have an object...
       //var temp:any = {}; // obj.constructor(); // we can't call obj.constructor because TypeScript defines this, so if we are dealing with a TypeScript object it might reset values.
       var temp = cloneConstructor ? new obj.constructor() : {};

       for (var key in tpl) {
           //if we are provided with a function to determine the value of this property, call it...
           if (isFunction(tpl[key])) {
               temp[key] = tpl[key](obj); //assign the result of the function call, passing in the value
           } else {
               //if our object has this property...
               if (obj[key] != undefined) {
                   if (Array.isArray(obj[key])) {
                       temp[key] = [];
                       for (var i = 0; i < obj[key].length; i++) {
                           temp[key].Push(cloneObjectByTemplate(obj[key][i], tpl[key], cloneConstructor));
                       }
                   } else {
                       temp[key] = cloneObjectByTemplate(obj[key], tpl[key], cloneConstructor);
                   }
               }
           }
       }

       return temp;
   }

Eine einfache Art, es zu nennen, wäre wie folgt:

var source = {
       a: "whatever",
       b: {
           x: "yeah",
           y: "haha"
       }
   };
   var template = {
       a: true, //we want to clone "a"
       b: {
           x: true //we want to clone "b.x" too
       }
   }; 
   var destination = cloneObjectByTemplate(source, template);

Wenn Sie eine Funktion verwenden möchten, um sicherzustellen, dass ein Attribut zurückgegeben wird, oder um sicherzustellen, dass es sich um einen bestimmten Typ handelt, verwenden Sie eine Vorlage wie diese. Anstatt { ID: true } zu verwenden, stellen wir eine Funktion zur Verfügung, die nur ID attribute des Quellobjekts kopiert, aber sicherstellt, dass es sich um eine Zahl handelt, auch wenn sie im Quellobjekt nicht vorhanden ist.

 var template = {
    ID: function (srcObj) {
        if(srcObj.ID == undefined){ return -1; }
        return parseInt(srcObj.ID.toString());
    }
}

Arrays werden gut klonen, aber wenn Sie möchten, können Sie Ihre eigene Funktion auch für diese individuellen Attribute verwenden und etwas Besonderes tun:

 var template = {
    tags: function (srcObj) {
        var tags = [];
        if (process.tags != undefined) {
            for (var i = 0; i < process.tags.length; i++) {

                tags.Push(cloneObjectByTemplate(
                  srcObj.tags[i],
                  { a : true, b : true } //another template for each item in the array
                );
            }
        }
        return tags;
    }
 }

Im obigen Beispiel kopiert unsere Vorlage nur das tags -Attribut des Quellobjekts, sofern es vorhanden ist (es wird angenommen, dass es sich um ein Array handelt), und für jedes Element in diesem Array wird die Klonfunktion aufgerufen, um es basierend einzeln zu klonen auf einer zweiten Vorlage, die nur die Attribute a und b jedes dieser Tag-Elemente kopiert.

Wenn Sie Objekte in den Knoten aufnehmen und aus dem Knoten entfernen und steuern möchten, welche Attribute dieser Objekte geklont werden, können Sie dies auf hervorragende Weise in node.js steuern, und der Code funktioniert auch im Browser.

Hier ist ein Beispiel für die Verwendung: http://jsfiddle.net/hjchyLt1/

3
Action Dan

Sie können den Funktionsabschluss verwenden, um alle Vorteile einer tiefen Kopie ohne eine tiefe Kopie zu nutzen. Es ist ein ganz anderes Paradigma, aber es funktioniert gut. Anstatt zu versuchen, ein vorhandenes Objekt zu kopieren, verwenden Sie einfach eine Funktion, um ein neues Objekt zu instanziieren, wenn Sie eines benötigen.

Erstellen Sie zunächst eine Funktion, die ein Objekt zurückgibt

function template() {
  return {
    values: [1, 2, 3],
    nest: {x: {a: "a", b: "b"}, y: 100}
  };
}

Erstellen Sie dann eine einfache flache Kopierfunktion

function copy(a, b) {
  Object.keys(b).forEach(function(key) {
    a[key] = b[key];
  });
}

Erstellen Sie ein neues Objekt und kopieren Sie die Eigenschaften der Vorlage darauf

var newObject = {}; 
copy(newObject, template());

Der obige Kopierschritt ist jedoch nicht erforderlich. Alles, was Sie tun müssen, ist Folgendes:

var newObject = template();

Nachdem Sie ein neues Objekt haben, testen Sie, welche Eigenschaften es hat:

console.log(Object.keys(newObject));

Dies zeigt an:

["values", "nest"]

Ja, dies sind die eigenen Eigenschaften von newObject, keine Verweise auf Eigenschaften eines anderen Objekts. Lassen Sie uns einfach überprüfen:

console.log(newObject.nest.x.b);

Dies zeigt an:

"b"

Das newObject hat alle Eigenschaften des Vorlagenobjekts erfasst, ist jedoch frei von jeglicher Abhängigkeitskette.

http://jsbin.com/ISUTIpoC/1/edit?js,console

Ich habe dieses Beispiel hinzugefügt, um eine Diskussion anzuregen. Bitte fügen Sie einige Kommentare hinzu :)

3
d13

Laut Airbnb JavaScript Style Guide mit 404 Mitwirkenden:

Bevorzugen Sie den Objektverbreitungsoperator gegenüber Object.assign für flache Objekte. Verwenden Sie den Operator object rest, um ein neues Objekt zu erhalten, bei dem bestimmte Eigenschaften weggelassen wurden.

// very bad
const original = { a: 1, b: 2 };
const copy = Object.assign(original, { c: 3 }); // this mutates `original` ಠ_ಠ
delete copy.a; // so does this

// bad
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }

// good
const original = { a: 1, b: 2 };
const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }

const { a, ...noA } = copy; // noA => { b: 2, c: 3 }

Außerdem möchte ich Sie warnen, dass Airbnb den Ansatz des Objektverbreitungs-Operators kaum empfiehlt. Beachten Sie, dass Microsoft Edge diese Funktion für 2018 noch nicht unterstützt.

ES2016 + Kompatibilitätstabelle >>

3
Zsolt Gulyás

Verwenden Sie lodash _.cloneDeep ().

Flaches Exemplar: lodash _.clone ()

Eine flache Kopie kann durch einfaches Kopieren der Referenz erstellt werden.

let obj1 = {
    a: 0,
    b: {
        c: 0,
        e: {
            f: 0
        }
    }
};
let obj3 = _.clone(obj1);
obj1.a = 4;
obj1.b.c = 4;
obj1.b.e.f = 100;

console.log(JSON.stringify(obj1));
//{"a":4,"b":{"c":4,"e":{"f":100}}}

console.log(JSON.stringify(obj3));
//{"a":0,"b":{"c":4,"e":{"f":100}}}

Shallow Copy: lodash _.clone()

Deep Copy: lodash _.cloneDeep ()

Felder werden dereferenziert: anstatt auf Objekte zu verweisen, die kopiert werden

let obj1 = {
    a: 0,
    b: {
        c: 0,
        e: {
            f: 0
        }
    }
};
let obj3 = _.cloneDeep(obj1);
obj1.a = 100;
obj1.b.c = 100;
obj1.b.e.f = 100;

console.log(JSON.stringify(obj1));
{"a":100,"b":{"c":100,"e":{"f":100}}}

console.log(JSON.stringify(obj3));
{"a":0,"b":{"c":0,"e":{"f":0}}}

Deep Copy: lodash _.cloneDeep()

2
Ashok R

Ich denke, dass Wiederholung mit Caching das Beste ist, was wir hier ohne Bibliotheken tun können.

Und unterschätzt WeakMap kommt zum Problem von Zyklen, bei denen das Speichern von Referenzpaaren auf alte und neue Objekte uns dabei helfen kann, ganz einfach einen Baum zu erstellen.

Ich habe das tiefe Klonen der DOM-Elemente verhindert, wahrscheinlich möchten Sie nicht die ganze Seite klonen :)

function deepCopy(object) {
    const cache = new WeakMap(); // Map of old - new references

    function copy(obj) {
        if (typeof obj !== 'object' ||
            obj === null ||
            obj instanceof HTMLElement
        )
            return obj; // primitive value or HTMLElement

        if (obj instanceof Date) 
            return new Date().setTime(obj.getTime());

        if (obj instanceof RegExp) 
            return new RegExp(obj.source, obj.flags);

        if (cache.has(obj)) 
            return cache.get(obj);

        const result = obj instanceof Array ? [] : {};

        cache.set(obj, result); // store reference to object before the recursive starts

        if (obj instanceof Array) {
            for(const o of obj) {
                 result.Push(copy(o));
            }
            return result;
        }

        const keys = Object.keys(obj); 

        for (const key of keys)
            result[key] = copy(obj[key]);

        return result;
    }

    return copy(object);
}

Einige Tests:

// #1
const obj1 = { };
const obj2 = { };
obj1.obj2 = obj2;
obj2.obj1 = obj1; // Trivial circular reference

var copy = deepCopy(obj1);
copy == obj1 // false
copy.obj2 === obj1.obj2 // false
copy.obj2.obj1.obj2 // and so on - no error (correctly cloned).

// #2
const obj = { x: 0 }
const clone = deepCopy({ a: obj, b: obj });
clone.a == clone.b // true

// #3
const arr = [];
arr[0] = arr; // A little bit weird but who cares
clone = deepCopy(arr)
clone == arr // false;
clone[0][0][0][0] == clone // true;

ANMERKUNG: Ich verwende Konstanten, for of loop, => operator und WeakMaps, um wichtigeren Code zu erstellen. Diese Syntax (ES6) wird von den heutigen Browsern unterstützt

2
Maciej Bukowski

Ich habe dies bei einem skalaren Objekt versucht und es funktioniert bei mir:

function binder(i) {
  return function () {
    return i;
  };
}

a=1;
b=binder(a)(); // copy value of a into b

alert(++a); // 2
alert(b); // still 1

Grüße.

2
John Sonderson

Verwenden von Standardeinstellungen (historisch spezifisch für nodejs, aber jetzt dank modernem JS vom Browser aus verwendbar):

import defaults from 'object.defaults';

const myCopy = defaults({}, myObject);
1
aberaud

Wenn Ihr Objekt keine zirkulären Abhängigkeiten enthält, empfehle ich die Verwendung einer der anderen Antworten oder Kopiermethoden von jQuery , da sie alle recht effektiv zu sein scheinen.

Wenn es zirkuläre Abhängigkeiten gibt (d. H. Zwei Unterobjekte sind miteinander verknüpft), sind Sie (aus theoretischer Sicht) sozusagen verschraubt keine Möglichkeit, dieses Problem elegant zu lösen .

1
Daniel Lew

Ok, dies ist möglicherweise die beste Option für flaches Kopieren. Befolgen Sie die vielen Beispiele unter Zuweisen, aber es behält auch die Vererbung und den Prototyp bei. Es ist auch so einfach und funktioniert für die meisten Array-ähnlichen Objekte, außer für Objekte mit Konstruktoranforderungen oder schreibgeschützten Eigenschaften. Dies bedeutet jedoch, dass es für TypedArrays-, RegExp-, Date-, Maps-, Set- und Object-Versionen von Grundelementen (Boolean, String usw.) kläglich fehlschlägt.

function copy ( a ) { return Object.assign( new a.constructor, a ) }

Wobei a ein beliebiges Objekt oder eine Instanz mit Klassenkonstruktion sein kann, aber wiederum nicht zuverlässig für Objekte, die spezielle Getter und Setter verwenden oder Konstruktoranforderungen haben, sondern für einfachere Situationen, die es rockt. Es funktioniert auch mit Argumenten.

Sie können es auch auf Grundelemente anwenden, um seltsame Ergebnisse zu erzielen, aber dann ... es sei denn, es ist nur ein nützlicher Hack, wen interessiert das?.

ergebnisse aus grundlegenden integrierten Objekt- und Array-Funktionen.

> a = { a: 'A', b: 'B', c: 'C', d: 'D' }
{ a: 'A', b: 'B', c: 'C', d: 'D' }
> b = copy( a )
{ a: 'A', b: 'B', c: 'C', d: 'D' }
> a = [1,2,3,4]
[ 1, 2, 3, 4 ]
> b = copy( a )
[ 1, 2, 3, 4 ]

Und scheitert an gemeinen get/setters, vom Konstruktor benötigten Argumenten oder schreibgeschützten Eigenschaften und an Sünden gegen den Vater.

> a = /\w+/g
/\w+/g
> b = copy( a )  // fails because source and flags are read-only
/(?:)/
> a = new Date ( '1/1/2001' )
2000-12-31T16:00:00.000Z
> b = copy( a )  // fails because Date using methods to get and set things
2017-02-04T14:44:13.990Z
> a = new Boolean( true )
[Boolean: true]
> b = copy( a )  // fails because of of sins against the father
[Boolean: false]
> a = new Number( 37 )
[Number: 37]
> b = copy( a )  // fails because of of sins against the father
[Number: 0]
> a = new String( 'four score and seven years ago our four fathers' )
[String: 'four score and seven years ago our four fathers']
> b = copy( a )  // fails because of of sins against the father
{ [String: ''] '0': 'f', '1': 'o', '2': 'u', '3': 'r', '4': ' ', '5': 's', '6': 'c', '7': 'o', '8': 'r', '9': 'e', '10': ' ', '11': 'a', '12': 'n', '13': 'd', '14': ' ', '15': 's', '16': 'e', '17': 'v', '18': 'e', '19': 'n', '20': ' ', '21': 'y', '22': 'e', '23': 'a', '24': 'r', '25': 's', '26': ' ', '27': 'a', '28': 'g', '29': 'o', '30': ' ', '31': 'o', '32': 'u', '33': 'r', '34': ' ', '35': 'f', '36': 'o', '37': 'u', '38': 'r', '39': ' ', '40': 'f', '41': 'a', '42': 't', '43': 'h', '44': 'e', '45': 'r', '46': 's' } 
1
Erich Horn

Hier ist eine moderne Lösung, die nicht die Fallstricke von Object.assign() aufweist (kopiert nicht per Referenz):

const cloneObj = (obj) => {
    return Object.keys(obj).reduce((dolly, key) => {
        dolly[key] = (obj[key].constructor === Object) ?
            cloneObj(obj[key]) :
            obj[key];
        return dolly;
    }, {});
};
1
ryanpcmcquen

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

const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };

const returnedTarget = Object.assign(target, source);

console.log(target);
// expected output: Object { a: 1, b: 4, c: 5 }

console.log(returnedTarget);
// expected output: Object { a: 1, b: 4, c: 5 }

Syntax

Object.assign(target, ...sources)

Die Methode Object.assign() kopiert nur aufzählbare und eigene Eigenschaften von einem Quellobjekt in ein Zielobjekt. Es verwendet [[Get]] für die Quelle und [[Set]] für das Ziel,

so wird es Getter und Setter hervorrufen. Daher werden Eigenschaften zugewiesen und keine neuen Eigenschaften kopiert oder definiert. Dies kann dazu führen, dass neue Eigenschaften nicht zu einem Prototyp zusammengeführt werden können, wenn die Zusammenführungsquellen Getter enthalten. Zum Kopieren von Eigenschaftsdefinitionen einschließlich ihrer Aufzählung in Prototypen sollten stattdessen Object.getOwnPropertyDescriptor() und Object.defineProperty() verwendet werden.

Sowohl Zeichenketten- als auch Symboleigenschaften werden kopiert.

Wenn im Fehlerfall beispielsweise eine Eigenschaft nicht schreibbar ist, wird ein TypeError ausgelöst, und das Zielobjekt kann geändert werden, wenn Eigenschaften hinzugefügt werden, bevor ein Fehler ausgelöst wird.

Beachten Sie, dass Object.assign() keine null oder undefined Quellwerte aktiviert.

1
Ashish

Um ein besseres Verständnis des Kopierens von Objekten zu unterstützen, dieser veranschaulichende Jsbin kann von Wert sein

class base {
  get under(){return true}
}

class a extends base {}

const b = {
  get b1(){return true},
  b: true
}

console.log('Object assign')
let t1 = Object.create(b)
t1.x = true
const c = Object.assign(t1, new a())
console.log(c.b1 ? 'prop value copied': 'prop value gone')
console.log(c.x ? 'assigned value copied': 'assigned value gone')
console.log(c.under ? 'inheritance ok': 'inheritance gone')
console.log(c.b1 ? 'get value unchanged' : 'get value lost')
c.b1 = false
console.log(c.b1? 'get unchanged' : 'get lost')
console.log('-----------------------------------')
console.log('Object assign  - order swopped')
t1 = Object.create(b)
t1.x = true
const d = Object.assign(new a(), t1)
console.log(d.b1 ? 'prop value copied': 'prop value gone')
console.log(d.x ? 'assigned value copied': 'assigned value gone')
console.log(d.under ? 'inheritance n/a': 'inheritance gone')
console.log(d.b1 ? 'get value copied' : 'get value lost')
d.b1 = false
console.log(d.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')
console.log('Spread operator')
t1 = Object.create(b)
t2 = new a()
t1.x = true
const e = { ...t1, ...t2 }
console.log(e.b1 ? 'prop value copied': 'prop value gone')
console.log(e.x ? 'assigned value copied': 'assigned value gone')
console.log(e.under ? 'inheritance ok': 'inheritance gone')
console.log(e.b1 ? 'get value copied' : 'get value lost')
e.b1 = false
console.log(e.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')
console.log('Spread operator on getPrototypeOf')
t1 = Object.create(b)
t2 = new a()
t1.x = true
const e1 = { ...Object.getPrototypeOf(t1), ...Object.getPrototypeOf(t2) }
console.log(e1.b1 ? 'prop value copied': 'prop value gone')
console.log(e1.x ? 'assigned value copied': 'assigned value gone')
console.log(e1.under ? 'inheritance ok': 'inheritance gone')
console.log(e1.b1 ? 'get value copied' : 'get value lost')
e1.b1 = false
console.log(e1.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')
console.log('keys, defineProperty, getOwnPropertyDescriptor')
f = Object.create(b)
t2 = new a()
f.x = 'a'
Object.keys(t2).forEach(key=> {
  Object.defineProperty(f,key,Object.getOwnPropertyDescriptor(t2, key))
})
console.log(f.b1 ? 'prop value copied': 'prop value gone')
console.log(f.x ? 'assigned value copied': 'assigned value gone')
console.log(f.under ? 'inheritance ok': 'inheritance gone')
console.log(f.b1 ? 'get value copied' : 'get value lost')
f.b1 = false
console.log(f.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')
console.log('defineProperties, getOwnPropertyDescriptors')
let g = Object.create(b)
t2 = new a()
g.x = 'a'
Object.defineProperties(g,Object.getOwnPropertyDescriptors(t2))
console.log(g.b1 ? 'prop value copied': 'prop value gone')
console.log(g.x ? 'assigned value copied': 'assigned value gone')
console.log(g.under ? 'inheritance ok': 'inheritance gone')
console.log(g.b1 ? 'get value copied' : 'get value lost')
g.b1 = false
console.log(g.b1? 'get copied' : 'get lost')
console.log('-----------------------------------')
1
TrevTheDev

In meinem Code definiere ich häufig eine function (_) für die Bearbeitung von Kopien, damit ich by value an Funktionen übergeben kann. Dieser Code erstellt eine tiefe Kopie, behält jedoch die Vererbung bei. Außerdem werden Unterkopien nachverfolgt, sodass selbstreferenzielle Objekte ohne Endlosschleife kopiert werden können. Fühlen Sie sich frei, es zu benutzen.

Es ist vielleicht nicht das eleganteste, aber es hat mich noch nicht enttäuscht.

_ = function(oReferance) {
  var aReferances = new Array();
  var getPrototypeOf = function(oObject) {
    if(typeof(Object.getPrototypeOf)!=="undefined") return Object.getPrototypeOf(oObject);
    var oTest = new Object();
    if(typeof(oObject.__proto__)!=="undefined"&&typeof(oTest.__proto__)!=="undefined"&&oTest.__proto__===Object.prototype) return oObject.__proto__;
    if(typeof(oObject.constructor)!=="undefined"&&typeof(oTest.constructor)!=="undefined"&&oTest.constructor===Object&&typeof(oObject.constructor.prototype)!=="undefined") return oObject.constructor.prototype;
    return Object.prototype;
  };
  var recursiveCopy = function(oSource) {
    if(typeof(oSource)!=="object") return oSource;
    if(oSource===null) return null;
    for(var i=0;i<aReferances.length;i++) if(aReferances[i][0]===oSource) return aReferances[i][1];
    var Copy = new Function();
    Copy.prototype = getPrototypeOf(oSource);
    var oCopy = new Copy();
    aReferances.Push([oSource,oCopy]);
    for(sPropertyName in oSource) if(oSource.hasOwnProperty(sPropertyName)) oCopy[sPropertyName] = recursiveCopy(oSource[sPropertyName]);
    return oCopy;
  };
  return recursiveCopy(oReferance);
};

// Examples:
Wigit = function(){};
Wigit.prototype.bInThePrototype = true;
A = new Wigit();
A.nCoolNumber = 7;
B = _(A);
B.nCoolNumber = 8; // A.nCoolNumber is still 7
B.bInThePrototype // true
B instanceof Wigit // true
1
Alec

Das Problem beim Kopieren eines Objekts, das möglicherweise auf sich selbst verweist, kann mit einer einfachen Überprüfung behoben werden. Fügen Sie diese Prüfung jedes Mal hinzu, wenn eine Kopieraktion stattfindet. Es mag langsam sein, aber es sollte funktionieren.

Ich benutze eine toType () - Funktion, um den Objekttyp explizit zurückzugeben. Ich habe auch meine eigene Funktion copyObj (), die in der Logik ziemlich ähnlich ist und alle drei Fälle Object (), Array () und Date () beantwortet.

Ich führe es in NodeJS aus.

NOCH NICHT GETESTET.

// Returns true, if one of the parent's children is the target.
// This is useful, for avoiding copyObj() through an infinite loop!
function isChild(target, parent) {
  if (toType(parent) == '[object Object]') {
    for (var name in parent) {
      var curProperty = parent[name];

      // Direct child.
      if (curProperty = target) return true;

      // Check if target is a child of this property, and so on, recursively.
      if (toType(curProperty) == '[object Object]' || toType(curProperty) == '[object Array]') {
        if (isChild(target, curProperty)) return true;
      }
    }
  } else if (toType(parent) == '[object Array]') {
    for (var i=0; i < parent.length; i++) {
      var curItem = parent[i];

      // Direct child.
      if (curItem = target) return true;

      // Check if target is a child of this property, and so on, recursively.
      if (toType(curItem) == '[object Object]' || toType(curItem) == '[object Array]') {
        if (isChild(target, curItem)) return true;
      }
    }
  }

  return false;     // Not the target.
}
0
James Koss

Ich weiß nicht, für welche Fälle dies nicht funktioniert, aber ich habe eine Kopie eines Arrays erhalten. Ich finde es süß :) Hoffe es hilft

copiedArr = origArr.filter(function(x){return true})
0
knowingpark

Um kreisförmige Objekte zu verarbeiten, die JSON.stringify nicht verarbeiten kann, können Sie eine Bibliothek mit dem Namen JSOG einbinden, die beliebige Diagramme im JSON-Format serialisiert und deserialisiert.

var clone = JSOG.parse(JSOG.stringify(original));

Es könnte auch interessant sein, JSOG für das Klonen mit diesem Trick zu patchen (im Moment habe ich keine Zeit, aber wenn jemand es ausprobieren möchte ...):

Serialisieren Sie eine einfache Funktion:

foo.f = function(a) { return a }
var stringForm = foo.f.toString() // "function (a) { return a }"

Eine Funktion deserialisieren:

eval("foo.f = " + stringForm)

Einige Konventionen (wahrscheinlich im Namen der Eigenschaft) zum Identifizieren von Funktionen gegenüber regulären Zeichenfolgen wären erforderlich (@func_f vielleicht).

Wenn die Funktion eine zweite Funktion aufruft, muss die zweite Funktion natürlich genauso vorhanden sein wie für das Original.

Das oben Gesagte ist jedoch sehr gefährlich, wenn Sie das serialisierte Formular von einer nicht vertrauenswürdigen Quelle akzeptieren möchten. Das Akzeptieren von Funktionen in beliebiger Form von einer nicht vertrauenswürdigen Quelle wäre jedoch gefährlich. Wenn Sie also daran interessiert sind, Funktionen zu klonen, muss bereits eine Vertrauensstellung hergestellt worden sein (oder Sie möchten bereits eine Sicherheitslücke schließen!).

Haftungsausschluss: Ich habe die Geschwindigkeit von JSOG stringify/parse im Vergleich zu JSON stringify/parse nicht getestet, aber es funktioniert mit den einfachen (kreisförmigen) Objekten, die ich getestet habe es mit.

0
Gus

Wenn Sie ein Objekt mit Funktionen haben, können Sie dies mit JSONfn tun, siehe http://www.eslinstructor.net/jsonfn/ .

var obj= {
    name:'Marvin',
    getName :  function(){
      return this.name;
    }
}
var cobj = JSONfn.parse(JSONfn.stringify(obj));
0
basis

Sie können Ihr Objekt ohne Änderung übergeordnetes Objekt klonen -

    /** [Object Extend]*/
    ( typeof Object.extend === 'function' ? undefined : ( Object.extend = function ( destination, source ) {
        for ( var property in source )
            destination[property] = source[property];
        return destination;
    } ) );
    /** [/Object Extend]*/
    /** [Object clone]*/
    ( typeof Object.clone === 'function' ? undefined : ( Object.clone = function ( object ) {
        return this.extend( {}, object );
    } ) );
    /** [/Object clone]*/

    let myObj = {
        a:1, b:2, c:3, d:{
            a:1, b:2, c:3
        }
    };

    let clone = Object.clone( myObj );

    clone.a = 10;

    console.log('clone.a==>', clone.a); //==> 10

    console.log('myObj.a==>', myObj.a); //==> 1 // object not modified here

    let clone2 = Object.clone( clone );

    clone2.a = 20;

    console.log('clone2.a==>', clone2.a); //==> 20

    console.log('clone.a==>', clone.a); //==> 10 // object not modified here
0
RKTUXYN

Ich bin auf diese Seite gekommen, weil ich die gleiche Frage hatte, aber ich verwende weder JQuery noch eine der Klonmethoden für meine eigenen Objekte.

Ich bin mir bewusst, dass meine Antwort nicht zu stark mit dieser Frage zusammenhängt, weil es ein anderer Ansatz ist. Anstatt Klonfunktionen zu verwenden, verwende ich eine Erstellungsfunktion. Es hat für mich aus folgenden (leider einschränkenden) Gründen funktioniert:

  1. Ich benutze meistens JSP-generiertes Javascript
  2. Ich weiß zu Beginn, welches Objekt generiert werden muss (in meinem Fall sind es Informationen aus einer Datenbank, die einmal abgerufen werden und häufiger im JS implementiert werden müssen.

Zuerst habe ich meine Objekte so definiert:

var obj= new Object();
obj.Type='Row';
obj.ID=1;
obj.Value='Blah blah';

Jetzt habe ich alles verschoben wie:

function getObjSelektor(id_nummer,selected){
var obj = document.createElement("select");
obj.setAttribute("id","Selektor_"+id_nummer);
obj.setAttribute("name","Selektor");
obj.setAttribute("size","1");

var obj_opt_1 = document.createElement("option");
obj_opt_1.setAttribute("value","1");
if(1==selected)
    posopval_opt_1.setAttribute("selected","selected");
obj_opt_1.innerHTML="Blah blah";
obj.appendChild(obj_opt_1);

var obj_opt_2 = document.createElement("option");
obj_opt_2.setAttribute("value","2");
if(2==selected)
    obj_opt_2.setAttribute("selected","selected");
obj_opt_2.innerHTML="2nd Row";
obj.appendChild(obj_opt_2);

...

return obj;
}

Und rufen Sie die Funktion im regulären Code auf:

myDiv.getObjSelektor(getObjSelektor(anotherObject.ID));

Wie gesagt, dies ist ein anderer Ansatz, der mein Problem für meine Zwecke gelöst hat.

0
Qohelet
//
// creates 'clone' method on context object
//
//  var 
//     clon = Object.clone( anyValue );
//
!((function (propertyName, definition) {
    this[propertyName] = definition();
}).call(
    Object,
    "clone",
    function () {
        function isfn(fn) {
            return typeof fn === "function";
        }

        function isobj(o) {
            return o === Object(o);
        }

        function isarray(o) {
            return Object.prototype.toString.call(o) === "[object Array]";
        }

        function fnclon(fn) {
            return function () {
                fn.apply(this, arguments);
            };
        }

        function owns(obj, p) {
            return obj.hasOwnProperty(p);
        }

        function isemptyobj(obj) {
            for (var p in obj) {
                return false;
            }
            return true;
        }

        function isObject(o) {
            return Object.prototype.toString.call(o) === "[object Object]";
        }
        return function (input) {
            if (isfn(input)) {
                return fnclon(input);
            } else if (isobj(input)) {
                var cloned = {};
                for (var p in input) {
                    owns(Object.prototype, p)
                    || (
                        isfn(input[p])
                        && ( cloned[p] = function () { return input[p].apply(input, arguments); } )
                        || ( cloned[p] = input[p] )
                    );
                }
                if (isarray(input)) {
                    cloned.length = input.length;
                    "concat every filter forEach indexOf join lastIndexOf map pop Push reduce reduceRight reverse shift slice some sort splice toLocaleString toString unshift"
                    .split(" ")
                    .forEach(
                      function (methodName) {
                        isfn( Array.prototype[methodName] )
                        && (
                            cloned[methodName] =
                            function () {
                                return Array.prototype[methodName].apply(cloned, arguments);
                            }
                        );
                      }
                    );
                }
                return isemptyobj(cloned)
                       ? (
                          isObject(input)
                          ? cloned
                          : input
                        )
                       : cloned;
            } else {
                return input;
            }
        };
    }
));
//
0
public override

Einfach

var restore = { name:'charlesi',
age:9}
var prev_data ={
name: 'charles'
age : 10
}

var temp = JSON.stringify(prev_data)
restore = JSON.parse(temp)

restore = {
name:'charlie',
age : 12}

ausgabe prev_data:

{
name: 'charles'
age : 10
} 
0

Aus den Apple JavaScript Coding Guidelines :

// Create an inner object with a variable x whose default
// value is 3.
function innerObj()
{
        this.x = 3;
}
innerObj.prototype.clone = function() {
    var temp = new innerObj();
    for (myvar in this) {
        // this object does not contain any objects, so
        // use the lightweight copy code.
        temp[myvar] = this[myvar];
    }
    return temp;
}

// Create an outer object with a variable y whose default
// value is 77.
function outerObj()
{
        // The outer object contains an inner object.  Allocate it here.
        this.inner = new innerObj();
        this.y = 77;
}
outerObj.prototype.clone = function() {
    var temp = new outerObj();
    for (myvar in this) {
        if (this[myvar].clone) {
            // This variable contains an object with a
            // clone operator.  Call it to create a copy.
            temp[myvar] = this[myvar].clone();
        } else {
            // This variable contains a scalar value,
            // a string value, or an object with no
            // clone function.  Assign it directly.
            temp[myvar] = this[myvar];
        }
    }
    return temp;
}

// Allocate an outer object and assign non-default values to variables in
// both the outer and inner objects.
outer = new outerObj;
outer.inner.x = 4;
outer.y = 16;

// Clone the outer object (which, in turn, clones the inner object).
newouter = outer.clone();

// Verify that both values were copied.
alert('inner x is '+newouter.inner.x); // prints 4
alert('y is '+newouter.y); // prints 16

Steve

0
Steve Harrison

Genau wie dieser Link sagt benutze diesen Code:
let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));

0
Pouria Moosavi

Wenn Ihr Objekt eine Klasse ist (z. B. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes ):

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

Dann haben Sie in copiedObject eine tief kopierte Instanz der Klasse originalObject mit all ihren Methoden.

0
j rdl

Wenn Sie TypeScript verwenden, ältere Webbrowser unterstützen müssen (und daher Object.assign nicht verwenden können) und keine Bibliothek mit eingebauter Klonmethode verwenden, können Sie sich ein combine erstellen. Helfer in ein paar Zeilen Code. Es kombiniert Objekte und wenn Sie nur eines haben, klonen Sie es einfach.

/** Creates a new object that combines the properties of the specified objects. */
function combine(...objs: {}[]) {
    const combined = {};
    objs.forEach(o => Object.keys(o).forEach(p => combined[p] = o[p]));
    return combined;
}
0
Edward Brey

Ich gebe eine Antwort auf diese Frage, da ich hier keine systemeigenen, rekursiven Implementierungen sehe, die das Problem der DOM -Elemente lösen.

Das Problem dabei ist, dass _<element>_ die Attribute parent und child besitzt, die mit parent und child Werten auf das Original verweisen. _<element>_, was entweder nendlich rekursiv oder zyklische Redundanz verursacht.

Wenn Ihr Objekt etwas sicheres und einfaches ist wie

_{
    '123':456
}
_

... dann wird jede andere Antwort hier wahrscheinlich funktionieren.

Aber wenn Sie haben ...

_{
    '123':<reactJSComponent>,
    '456':document.createElement('div'),
}
_

... dann brauchst du so etwas:

_    // cloneVariable() : Clone variable, return null for elements or components.
var cloneVariable = function (args) {
    const variable = args.variable;

    if(variable === null) {
            return null;
    }

    if(typeof(variable) === 'object') {
            if(variable instanceof HTMLElement || variable.nodeType > 0) {
                    return null;
            }

            if(Array.isArray(variable)) {
                    var arrayclone = [];

                    variable.forEach((element) => {
                            arrayclone.Push(cloneVariable({'variable':element}));
                    });

                    return arrayclone;
            }

            var objectclone = {};

            Object.keys(variable).forEach((field) => {
                    objectclone[field] = cloneVariable({'variable':variable[field]});
            });

            return objectclone;
    }

    return variable;
}
_
0
HoldOffHunger