Frage Wie klemme ich ein JavaScript-Objekt richtig?


Ich habe ein Objekt, x. Ich möchte es als Objekt kopieren y, so dass sich ändert y nicht ändern x. Ich habe festgestellt, dass das Kopieren von Objekten, die von integrierten JavaScript-Objekten abgeleitet sind, zu zusätzlichen unerwünschten Eigenschaften führt. Das ist kein Problem, da ich eines meiner eigenen, literal konstruierten Objekte kopiere.

Wie klemme ich ein JavaScript-Objekt richtig?


2395


Ursprung


Antworten:


Aktualisierte Antwort

Benutz einfach Objekt.Zuordnung () wie vorgeschlagen Hier

Aber seien Sie sich bewusst, dass dies nur eine flache Kopie ist. Geschachtelte Objekte werden weiterhin als Referenzen kopiert.


Veraltete Antwort

Dies für jedes Objekt in JavaScript zu tun, wird nicht einfach oder einfach sein. Sie werden auf das Problem stoßen, dass Attribute vom Prototyp des Objekts falsch aufgenommen werden, die im Prototyp verbleiben sollten und nicht in die neue Instanz kopiert werden. Wenn Sie z. B. ein hinzufügen clone Methode zu Object.prototypeWie einige Antworten zeigen, müssen Sie dieses Attribut explizit überspringen. Aber was ist, wenn andere zusätzliche Methoden hinzugefügt werden? Object.prototypeoder andere Zwischenprototypen, von denen Sie nichts wissen? In diesem Fall werden Sie Attribute kopieren, die Sie nicht verwenden sollten. Daher müssen Sie unvorhergesehene, nicht lokale Attribute mit der Option "Nicht zutreffend" ermitteln hasOwnProperty Methode.

Zusätzlich zu nicht aufzählbaren Attributen tritt ein schwierigeres Problem auf, wenn Sie versuchen, Objekte mit ausgeblendeten Eigenschaften zu kopieren. Beispielsweise, prototype ist eine versteckte Eigenschaft einer Funktion. Außerdem wird der Prototyp eines Objekts mit dem Attribut referenziert __proto__, die ebenfalls ausgeblendet ist und nicht von einer For / In-Schleife kopiert wird, die über die Attribute des Quellobjekts iteriert. Ich denke __proto__ könnte spezifisch für den JavaScript-Interpreter von Firefox sein und es könnte etwas anderes in anderen Browsern sein, aber Sie bekommen 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 entdecken.

Ein weiterer Haken bei der Suche nach einer eleganten Lösung ist das Problem, die Prototyp-Vererbung korrekt einzurichten. Wenn der Prototyp des Quellobjekts ist Object, dann einfach ein neues allgemeines Objekt mit erstellen {} funktioniert, aber wenn der Prototyp der Quelle ein Nachkomme davon ist Object, dann wirst du die zusätzlichen Mitglieder dieses Prototyps vermissen, die du mit der hasOwnProperty Filter, oder die in dem Prototyp waren, waren aber in erster Linie nicht aufzählbar. Eine Lösung könnte darin bestehen, das Quellobjekt aufzurufen constructor Eigenschaft, um das anfängliche Kopierobjekt zu erhalten und dann über die Attribute zu kopieren, aber dann erhalten Sie immer noch nicht aufzählbare Attribute. Beispiel: a Date Objekt speichert seine Daten als verborgenes Mitglied:

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 wird 5 Sekunden hinter dem sein d2. Eine Möglichkeit, eins zu machen Date das gleiche wie das andere ist, indem man den setTime Methode, aber das ist spezifisch für die Date Klasse. Ich denke nicht, dass es eine kugelsichere allgemeine Lösung für dieses Problem gibt, obwohl ich glücklich wäre, falsch zu liegen!

Wenn ich eine allgemeine Tiefenkopie implementieren musste, endete ich mit der Annahme, dass ich nur eine Ebene kopieren müsste Object, Array, Date, String, Number, oder Boolean. Die letzten 3 Typen sind unveränderlich, so dass ich eine seichte Kopie durchführen und mich nicht darum kümmern könnte, dass sie sich ändert. Ich nahm weiter an, dass alle Elemente enthalten in Object oder Array wäre auch einer der 6 einfachen Typen in dieser Liste. Dies kann mit einem Code wie dem Folgenden 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, es gibt nicht mehr als eine Referenz auf dieselben Daten im Objekt. Beispielsweise:

// 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 zwar 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.


1308



Mit jQuery können Sie flache Kopie mit erweitern:

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

Nachfolgende Änderungen am "copyedObject" wirken sich nicht auf das "originalObject" aus und umgekehrt.

Oder um a zu machen tiefe Kopie:

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

712



Wenn Sie keine Funktionen in Ihrem Objekt verwenden, kann ein einfacher Liner wie folgt aussehen:

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

Dies funktioniert für alle Arten von Objekten, die Objekte, Arrays, Strings, Booleans und Zahlen enthalten.

Siehe auch dieser Artikel über den strukturierten Klonalgorithmus von Browsern Dies wird verwendet, wenn Nachrichten an und von einem Worker gesendet werden. Es enthält auch eine Funktion für das tiefe Klonen.


687



In ECMAScript 6 gibt es Objekt.zuweisen Methode, die Werte aller aufzählbaren eigenen Eigenschaften von einem Objekt in ein anderes kopiert. Beispielsweise:

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

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


508



Es gibt viele Antworten, aber keine erwähnt Object.create aus ECMAScript 5, das Ihnen zwar keine exakte Kopie liefert, aber die Quelle als Prototyp des neuen Objekts setzt.

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

  1. Wo eine solche Vererbung nützlich ist (duh!)
  2. Das Quellobjekt wird nicht geändert, wodurch die Beziehung zwischen den beiden Objekten nicht beeinträchtigt wird.

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 nativ, also keine Schleifenbildung, keine Rekursion. Ältere Browser benötigen jedoch ein Polyfill.


113



Eine elegante Möglichkeit, ein JavaScript-Objekt in einer Codezeile zu klonen

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

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

Das Polyfill um ältere Browser zu unterstützen:

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

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

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

105



Pro MDN:

  • Wenn Sie eine flache Kopie verwenden möchten, verwenden Sie Object.assign({}, a)
  • Für "tiefe" Kopie verwenden JSON.parse(JSON.stringify(a))

Es gibt keine Notwendigkeit für externe Bibliotheken, aber Sie müssen überprüfen Browserkompatibilität zuerst.


103