So klonen Sie eine Javascript ES6-Klasseninstanz

Lesezeit: 9 Minuten

Benutzer-Avatar
Tom

Wie klone ich eine Javascript-Klasseninstanz mit ES6.

Ich interessiere mich nicht für Lösungen, die auf jquery oder $extend basieren.

Ich habe ziemlich alte Diskussionen über das Klonen von Objekten gesehen, die darauf hindeuten, dass das Problem ziemlich kompliziert ist, aber mit ES6 bietet sich eine sehr einfache Lösung an – ich werde sie unten einfügen und sehen, ob die Leute es für zufriedenstellend halten.

Bearbeiten: Es wird vorgeschlagen, dass meine Frage ein Duplikat ist. Ich habe diese Antwort gesehen, aber sie ist 7 Jahre alt und beinhaltet sehr komplizierte Antworten mit pre-ES6 js. Ich schlage vor, dass meine Frage, die ES6 zulässt, eine dramatisch einfachere Lösung hat.

  • Wenn Sie eine neue Antwort auf eine alte Frage zu Stack Overflow haben, fügen Sie diese Antwort bitte zur ursprünglichen Frage hinzu, erstellen Sie nicht einfach eine neue.

    – Ketzeraffe

    4. Januar 2017 um 23:41 Uhr

  • Ich sehe das Problem, mit dem Tom konfrontiert ist / war, da ES6-Klasseninstanzen anders funktionieren als “normale” Objekte.

    – CherryNerd

    4. Januar 2017 um 23:48 Uhr

  • Außerdem stürzt das erste Stück Code in der akzeptierten Antwort, die Ihr “mögliches Duplikat” bereitstellt, tatsächlich ab, wenn ich versuche, es über eine Instanz einer ES6-Klasse auszuführen

    – CherryNerd

    4. Januar 2017 um 23:59 Uhr

  • Ich denke, dies ist kein Duplikat, denn obwohl die ES6-Klasseninstanz ein Objekt ist, ist nicht jedes Objekt eine ES6-Klasseninstanz und daher befasst sich die andere Frage nicht mit dem Problem dieser Frage.

    – Tomáš Zato – Wiedereinsetzung von Monica

    28. Oktober 2017 um 17:27 Uhr

  • Es ist kein Duplikat. Die andere Frage betraf rein Objects als Datenhalter verwendet. Hier geht es um ES6 classes und das Problem, die Informationen zum Klassentyp nicht zu verlieren. Es braucht eine andere Lösung.

    – flori

    28. Oktober 2017 um 19:54 Uhr


Benutzer-Avatar
flori

Es ist kompliziert; Ich habe viel ausprobiert! Am Ende hat dieser Einzeiler für meine benutzerdefinierten ES6-Klasseninstanzen funktioniert:

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

Es vermeidet das Setzen des Prototyps, weil Sie sagen es verlangsamt den Code sehr.

Es unterstützt Symbole, ist aber nicht perfekt für Getter/Setter und arbeitet nicht mit nicht aufzählbaren Eigenschaften (siehe Object.assign()-Dokumente). Auch das Klonen grundlegender interner Klassen (wie Array, Date, RegExp, Map usw.) scheint leider oft eine individuelle Handhabung zu erfordern.

Fazit: Es ist eine Sauerei. Hoffen wir, dass es eines Tages eine native und saubere Clone-Funktionalität geben wird.

  • Dadurch werden keine statischen Methoden kopiert, da sie eigentlich keine aufzählbaren eigenen Eigenschaften sind.

    – Herr Lavalamp

    22. Dezember 2017 um 6:26 Uhr

  • @Mr.Lavalamp und wie kann man (auch) die statischen Methoden kopieren?

    – flori

    23. Dezember 2017 um 12:53 Uhr

  • dadurch werden die Arrays zerstört! Es konvertiert alle Arrays in Objekte mit den Schlüsseln “0”, “1”, ….

    – Vahid

    19. Oktober 2018 um 10:29 Uhr

  • @KeshaAntonov Möglicherweise finden Sie eine Lösung mit den Methoden typeof und Array. Ich selbst habe es vorgezogen, alle Eigenschaften manuell zu klonen.

    – Vahid

    21. Oktober 2018 um 18:24 Uhr

  • Erwarten Sie nicht, dass Eigenschaften geklont werden, die selbst Objekte sind: jsbin.com/qeziwetexu/edit?js, Konsole

    – jduhls

    13. November 2018 um 20:12 Uhr

const clone = Object.assign( {}, instanceOfBlah );
Object.setPrototypeOf( clone, Blah.prototype );

Beachten Sie die Eigenschaften von Objektzuweisung: Es kopiert oberflächlich und kopiert keine Klassenmethoden.

Wenn Sie eine tiefe Kopie oder mehr Kontrolle über die Kopie wünschen, dann gibt es die Lodash-Klonfunktionen.

  • Seit Object.create erstellt neues Objekt mit vorgegebenem Prototyp, warum dann nicht einfach const clone = Object.assign(Object.create(instanceOfBlah), instanceOfBlah). Auch Klassenmethoden werden kopiert.

    – Barbatus

    19. Februar 2017 um 11:21 Uhr


  • @barbatus Das verwendet jedoch den falschen Prototyp, Blah.prototype != instanceOfBlah. Du solltest benutzen Object.getPrototypeOf(instanceOfBlah)

    – Bergi

    15. Juli 2017 um 1:56 Uhr

  • @Bergi nein, die ES6-Klasseninstanz hat nicht immer einen Prototyp. Kasse codepen.io/techniq/pen/qdZeZm dass es auch mit der Instanz funktioniert.

    – Barbatus

    2. September 2017 um 7:52 Uhr

  • @barbatus Entschuldigung, was? Ich folge nicht. Alle Instanzen haben einen Prototyp, das macht sie zu Instanzen. Probieren Sie den Code aus Floris Antwort aus.

    – Bergi

    2. September 2017 um 8:55 Uhr


  • @Bergi Ich denke, es hängt von der Babel-Konfiguration oder so ab. Ich implementiere gerade eine reaktive native App und Instanzen ohne geerbte Eigenschaften haben dort den Prototyp null. Auch wie man hier sieht developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… Es ist möglich, dass getPrototypeOf null zurückgibt.

    – Barbatus

    2. September 2017 um 8:59 Uhr


Ich mag fast alle Antworten. Ich hatte dieses Problem und um es zu lösen, würde ich es manuell tun, indem ich a definierte clone() -Methode und darin würde ich das gesamte Objekt von Grund auf neu erstellen. Für mich macht das Sinn, da das resultierende Objekt natürlich vom gleichen Typ wie das geklonte Objekt sein wird.

Beispiel mit Typoskript:

export default class ClassName {
    private name: string;
    private anotherVariable: string;
   
    constructor(name: string, anotherVariable: string) {
        this.name = name;
        this.anotherVariable = anotherVariable;
    }

    public clone(): ClassName {
        return new ClassName(this.name, this.anotherVariable);
    }
}

Ich mag diese Lösung, weil sie eher „objektorientiert“ aussieht

  • Dies ist in der Tat der richtige Weg. Es ist sehr schwer, einen Klonmechanismus zu bekommen, der generisch funktioniert. Es ist unmöglich, es für jeden einzelnen Fall richtig zum Laufen zu bringen. Es wird immer seltsame und inkonsistente Klassen geben. Sicherzustellen, dass Ihre Objekte selbst klonbar sind, ist also der einzige Weg, um sicher zu sein. Als Alternative (oder Ergänzung) ist es möglich, eine Methode zu haben, die das Klonen durchführt aus eine Instanz, so etwas wie public static clone(instance: MyClass): MyClass) die die gleiche Idee hat, das Klonen speziell zu handhaben, nur um es außerhalb der Instanz zu machen.

    – VLAZ

    25. November 2021 um 13:56 Uhr

  • Dies ist eine großartige Antwort, und der obige Kommentar ist ein großartiger Vorschlag. Es ist auch erwähnenswert, dass Objekt- und Array-Eigenschaften als Referenz übergeben werden, also müssten Sie diese ebenfalls klonen oder riskieren, unerwartete Nebenwirkungen zu erleiden! Hier eine Zusammenfassung zur Veranschaulichung: gist.github.com/sscovil/def81066dc59e6ff5084a499d9855253

    – Shaun Scovil

    22. Februar um 21:02 Uhr

Benutzer-Avatar
Cassio Seffrin

TLDR;

// Use this approach
//Method 1 - clone will inherit the prototype methods of the original.
    let cloneWithPrototype = Object.assign(Object.create(Object.getPrototypeOf(original)), original); 

In Javascript wird es nicht empfohlen, Erweiterungen des Prototyps vorzunehmen, da dies zu Problemen führt, wenn Sie Tests an Ihrem Code/Ihren Komponenten durchführen. Die Unit-Test-Frameworks übernehmen nicht automatisch Ihre Prototyp-Erweiterungen. Es ist also keine gute Praxis. Weitere Erklärungen zu Prototyperweiterungen finden Sie hier. Warum ist das Erweitern von nativen Objekten eine schlechte Praxis?

Um Objekte in JavaScript zu klonen, gibt es keinen einfachen oder direkten Weg. Hier ist eine erste Instanz mit “Shallow Copy”:

1 -> Flacher Klon:

class Employee {
    constructor(first, last, street) {
        this.firstName = first;
        this.lastName = last;
        this.address = { street: street };
    }

    logFullName() {
        console.log(this.firstName + ' ' + this.lastName);
    }
}

let original = new Employee('Cassio', 'Seffrin', 'Street A, 23');

//Method 1 - clone will inherit the prototype methods of the original.
let cloneWithPrototype = Object.assign(Object.create(Object.getPrototypeOf(original)), original); 

//Method 2 - object.assing() will not clone the Prototype.
let cloneWithoutPrototype =  Object.assign({},original); 

//Method 3 - the same of object assign but shorter syntax using "spread operator"
let clone3 = { ...original }; 

//tests
cloneWithoutPrototype.firstName="John";
cloneWithoutPrototype.address.street="Street B, 99"; //will not be cloned

Ergebnisse:

original.logFullName();

Ergebnis: Cassio Seffrin

cloneWithPrototype.logFullName();

Ergebnis: Cassio Seffrin

ursprüngliche.Adresse.Straße;

result: ‘Street B, 99’ // Beachten Sie, dass das ursprüngliche Unterobjekt geändert wurde

Hinweis: Wenn die Instanz Closures als eigene Eigenschaften hat, wird diese Methode sie nicht umschließen. (Lesen Sie mehr über Schließungen) Und außerdem wird das Unterobjekt “Adresse” nicht geklont.

cloneWithoutPrototype.logFullName()

Wird nicht funktionieren. Der Klon erbt keine der Prototypmethoden des Originals.

cloneWithPrototype.logFullName()

wird funktionieren, da der Klon auch seine Prototypen kopiert.

So klonen Sie Arrays mit Object.assign:

let cloneArr = array.map((a) => Object.assign({}, a));

Array mit ECMAScript-Spread-Syntax klonen:

let cloneArrSpread = array.map((a) => ({ ...a }));

2 -> Deep Clone:

Um eine komplett neue Objektreferenz zu archivieren, können wir JSON.stringify() verwenden, um das ursprüngliche Objekt als String zu parsen und danach zurück zu JSON.parse() zu parsen.

let deepClone = JSON.parse(JSON.stringify(original));

Beim Deep Clone bleiben die Verweise auf die Adresse erhalten. Die deepClone-Prototypen gehen jedoch verloren, daher funktioniert deepClone.logFullName() nicht.

3 -> Bibliotheken von Drittanbietern:

Eine weitere Option ist die Verwendung von Bibliotheken von Drittanbietern wie Loadash oder Underscore. Sie erstellen ein neues Objekt und kopieren jeden Wert vom Original in das neue Objekt, wobei die Referenzen im Speicher bleiben.

Unterstrich: let cloneUnderscore = _(original).clone();

Loadash-Klon: var cloneLodash = _.cloneDeep(original);

Der Nachteil von Lodash oder Unterstrich war die Notwendigkeit, einige zusätzliche Bibliotheken in Ihr Projekt aufzunehmen. Sie sind jedoch gute Optionen und liefern auch Hochleistungsergebnisse.

Erstellen Sie die Kopie des Objekts mit demselben Prototyp und denselben Eigenschaften wie das ursprüngliche Objekt.

function clone(obj) {
  return Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj))
}

Funktioniert mit nicht aufzählbaren Eigenschaften, Gettern, Settern usw. Kann interne Slots nicht klonen, die viele eingebaute Javascript-Typen haben (z. B. Array, Map, Proxy)

  • Dies ist ein guter Ansatz, da ein Großteil der für all dies erforderlichen Verarbeitung an JavaScript delegiert wird. Es hat jedoch ein Problem mit potenziellen Objektwerten, da sie zwischen dem Original und dem geklonten Objekt geteilt würden. Beispielsweise wird ein Array-Wert von beiden Instanzen aktualisiert.

    – VLAZ

    25. November 2021 um 13:36 Uhr

Benutzer-Avatar
Mohammad Daneshamooz

Wenn wir mehrere Klassen mit Erweiterungen voneinander haben, ist die beste Lösung für das Klonen jeder Instanz, eine Funktion zu definieren, um eine neue Instanz dieses Objekts in seiner Klassendefinition zu erstellen, wie:

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  clone() {
    return new Point(this.x, this.y);
  }
}
class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y);
    this.color = color;
  }
  clone() {
    return new ColorPoint(
      this.x, this.y, this.color.clone()); // (A)
  }
}

Jetzt kann ich die Klonfunktion (0 von Objekten wie:

let p = new ColorPoint(10,10,'red');
let pclone=p.clone();

  • Dies ist ein guter Ansatz, da ein Großteil der für all dies erforderlichen Verarbeitung an JavaScript delegiert wird. Es hat jedoch ein Problem mit potenziellen Objektwerten, da sie zwischen dem Original und dem geklonten Objekt geteilt würden. Beispielsweise wird ein Array-Wert von beiden Instanzen aktualisiert.

    – VLAZ

    25. November 2021 um 13:36 Uhr

Versuche dies:

function copy(obj) {
   //Edge case
   if(obj == null || typeof obj !== "object") { return obj; }

   var result = {};
   var keys_ = Object.getOwnPropertyNames(obj);

   for(var i = 0; i < keys_.length; i++) {
       var key = keys_[i], value = copy(obj[key]);
       result[key] = value;
   }

   Object.setPrototypeOf(result, obj.__proto__);

   return result;
}

//test
class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
};

var myPoint = new Point(0, 1);

var copiedPoint = copy(myPoint);

console.log(
   copiedPoint,
   copiedPoint instanceof Point,
   copiedPoint === myPoint
);

Da es verwendet Object.getOwnPropertyNameswerden auch nicht aufzählbare Eigenschaften hinzugefügt.

1015300cookie-checkSo klonen Sie eine Javascript ES6-Klasseninstanz

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy