Ist dies eine gute Möglichkeit, ein Objekt in ES6 zu klonen?

155

Das Googeln nach "Javascript-Klonobjekt" bringt einige wirklich seltsame Ergebnisse, einige sind hoffnungslos veraltet und andere einfach zu komplex, ist es nicht so einfach wie nur:

let clone = {...original};

Ist daran etwas falsch?

Dmitry Fadeev
quelle
1
Dies ist nicht legal ES6. Wenn dies nicht der Fall wäre, ist dies kein Klon: Sowohl Ihr Klon als auch Ihre ursprünglichen Eigenschaften weisen jetzt auf dieselben Dinge hin. Zum Beispiel original = { a: [1,2,3] }gibt Ihnen einen Klon mit clone.abuchstäblich Sein original.a. Modifikation durch entweder cloneoder originalmodifiziert dasselbe , also nein, das ist schlecht =)
Mike 'Pomax' Kamermans
2
@AlbertoRivera Es ist ein bisschen gültiges JavaScript, da es sich um einen Vorschlag der Stufe 2 handelt , der wahrscheinlich eine zukünftige Ergänzung des JavaScript-Standards darstellt.
Frxstrem
@Frxstrem mit der Frage zu ES6, dies ist nicht gültig JavaScript =)
Mike 'Pomax' Kamermans
3
Flaches oder tiefes Klonen?
Felix Kling
2
Sie haben Recht, es ist nicht gültig ES6, es ist gültig ES9 . developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
mikemaccana

Antworten:

240

Dies ist gut für das flache Klonen . Die Objektverteilung ist ein Standardbestandteil von ECMAScript 2018 .

Für das Deep Cloning benötigen Sie eine andere Lösung .

const clone = {...original} zu flachem Klon

const newobj = {...original, prop: newOne} unveränderlich eine weitere Requisite zum Original hinzufügen und als neues Objekt speichern.

Mark Shust an der M.academy
quelle
18
Ist dies jedoch nicht nur ein flacher Klon? Wie in werden Eigenschaften nicht rekursiv geklont, oder? Daher ändert original.innerObject === clone.innerObject und das Ändern von original.innerObject.property clone.innerObject.property.
Milanio
18
Ja, das ist ein flacher Klon. Wenn Sie einen tiefen Klon wollen, müssen SieJSON.parse(JSON.stringify(input))
Mark Shust an der M.academy
8
/! \ JSON.parse (JSON.stringify (Eingabe)) bringt Daten durcheinander, undefiniert, ... Es ist nicht die Silberkugel zum Klonen! Siehe: maxpou.fr/immutability-js-without-library
Guillaume
1
Ist der Hack JSON.stringify () / JSON.parse () wirklich die empfohlene Methode, um ein Objekt in ES6 tief zu klonen? Ich sehe es immer wieder empfohlen. Störend.
Solvitieg
3
@MarkShust funktioniert JSON.parse(JSON.stringify(input))nicht, denn wenn es dort functionsoder infinityals Werte vorhanden ist, wird es einfach nullan ihrer Stelle zugewiesen . Es wird nur funktionieren, wenn die Werte einfach sind literalsund nicht functions.
BackslashN
65

BEARBEITEN: Als diese Antwort veröffentlicht wurde, war die {...obj}Syntax in den meisten Browsern nicht verfügbar. Heutzutage sollten Sie damit einverstanden sein (es sei denn, Sie müssen IE 11 unterstützen).

Verwenden Sie Object.assign.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

var obj = { a: 1 };
var copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }

Dies macht jedoch keinen tiefen Klon. Es gibt noch keine native Methode zum tiefen Klonen.

BEARBEITEN: Wie @Mike 'Pomax' Kamermans in den Kommentaren erwähnt hat, können Sie einfache Objekte (dh keine Prototypen, Funktionen oder Zirkelverweise) mit tief klonen JSON.parse(JSON.stringify(input))

Alberto Rivera
quelle
19
Es gibt eine, vorausgesetzt, Ihr Objekt ist ein echtes Objektliteral und reine Daten. In diesem Fall JSON.parse(JSON.stringify(input))handelt es sich um einen richtigen tiefen Klon. In dem Moment, in dem Prototypen, Funktionen oder Zirkelverweise im Spiel sind, funktioniert diese Lösung jedoch nicht mehr.
Mike 'Pomax' Kamermans
@ Mike'Pomax'Kamermans Das stimmt. Der Verlust der Funktionalität für Getter und Setter ist jedoch schrecklich ...
Alberto Rivera
Wenn Sie eine generische Funktion benötigen, um ein Objekt tief zu klonen, lesen Sie stackoverflow.com/a/13333781/560114 .
Matt Browne
1
Es gibt jetzt eine Möglichkeit, nativ tiefes Klonen durchzuführen .
Dan Dascalescu
1
@ DanDascalescu, obwohl es experimentell ist, sieht es ziemlich vielversprechend aus. Danke für die Information!
Alberto Rivera
4

Wenn die von Ihnen verwendeten Methoden mit Objekten mit Datentypen wie Datum nicht gut funktionieren , versuchen Sie dies

Importieren _

import * as _ from 'lodash';

Tiefes Klonobjekt

myObjCopy = _.cloneDeep(myObj);
shaheer shukur
quelle
Nur import _ from 'lodash';ist ausreichend. Aber +1 für die Antwort "Rad nicht neu erfinden".
Rustyx
lodash ist aufgebläht. Wirklich keine Notwendigkeit, lodash nur für eine einfache tiefe Kopie einzuziehen. Viele andere Lösungen hier. Dies ist eine wirklich schlechte Antwort für Webentwickler, die eine schlanke App erstellen möchten.
Jason Rice
3

Wenn Sie json.parse (json.stringify (Objekt)) nicht verwenden möchten, können Sie rekursiv Schlüsselwertkopien erstellen:

function copy(item){
  let result = null;
  if(!item) return result;
  if(Array.isArray(item)){
    result = [];
    item.forEach(element=>{
      result.push(copy(element));
    });
  }
  else if(item instanceof Object && !(item instanceof Function)){ 
    result = {};
    for(let key in item){
      if(key){
        result[key] = copy(item[key]);
      }
    }
  }
  return result || item;
}

Der beste Weg ist jedoch, eine Klasse zu erstellen, die einen Klon von sich selbst zurückgeben kann

class MyClass{
    data = null;
    constructor(values){ this.data = values }
    toString(){ console.log("MyClass: "+this.data.toString(;) }
    remove(id){ this.data = data.filter(d=>d.id!==id) }
    clone(){ return new MyClass(this.data) }
}
marcel
quelle
2

Nach der Antwort von @marcel stellte ich fest, dass einige Funktionen des geklonten Objekts noch fehlten. z.B

function MyObject() {
  var methodAValue = null,
      methodBValue = null

  Object.defineProperty(this, "methodA", {
    get: function() { return methodAValue; },
    set: function(value) {
      methodAValue = value || {};
    },
    enumerable: true
  });

  Object.defineProperty(this, "methodB", {
    get: function() { return methodAValue; },
    set: function(value) {
      methodAValue = value || {};
    }
  });
}

Wo auf MyObject konnte ich Methode A klonen, aber Methode B wurde ausgeschlossen. Dies geschah, weil es fehlt

enumerable: true

was bedeutete, dass es nicht in auftauchte

for(let key in item)

Stattdessen wechselte ich zu

Object.getOwnPropertyNames(item).forEach((key) => {
    ....
  });

Dies beinhaltet nicht aufzählbare Schlüssel.

Ich fand auch, dass der Prototyp ( Proto ) nicht geklont wurde. Dafür habe ich letztendlich verwendet

if (obj.__proto__) {
  copy.__proto__ = Object.assign(Object.create(Object.getPrototypeOf(obj)), obj);
}

PS: Frustrierend, dass ich keine eingebaute Funktion dafür finden konnte.

Shane Gannon
quelle
1

Sie können es auch so machen,

let copiedData = JSON.parse(JSON.stringify(data));
rafee_que_
quelle
-1
We can do that with two way:
1- First create a new object and replicate the structure of the existing one by iterating 
 over its properties and copying them on the primitive level.

let user = {
     name: "John",
     age: 30
    };

    let clone = {}; // the new empty object

    // let's copy all user properties into it
    for (let key in user) {
      clone[key] = user[key];
    }

    // now clone is a fully independant clone
    clone.name = "Pete"; // changed the data in it

    alert( user.name ); // still John in the original object

2- Second we can use the method Object.assign for that 
    let user = { name: "John" };
    let permissions1 = { canView: true };
    let permissions2 = { canEdit: true };

    // copies all properties from permissions1 and permissions2 into user
    Object.assign(user, permissions1, permissions2);

  -Another example

    let user = {
      name: "John",
      age: 30
    };

    let clone = Object.assign({}, user);
It copies all properties of user into the empty object and returns it. Actually, the same as the loop, but shorter.

Object.assign () erstellt jedoch keinen tiefen Klon

let user = {
  name: "John",
  sizes: {
    height: 182,
    width: 50
  }
};

let clone = Object.assign({}, user);

alert( user.sizes === clone.sizes ); // true, same object

// user and clone share sizes
user.sizes.width++;       // change a property from one place
alert(clone.sizes.width); // 51, see the result from the other one

Um dies zu beheben, sollten wir die Klonschleife verwenden, die jeden Wert von Benutzer [Schlüssel] untersucht und, wenn es sich um ein Objekt handelt, auch dessen Struktur repliziert. Das nennt man ein "tiefes Klonen".

Es gibt einen Standardalgorithmus für das tiefe Klonen, der den obigen Fall und komplexere Fälle behandelt, den strukturierten Klonalgorithmus . Um das Rad nicht neu zu erfinden, können wir eine funktionierende Implementierung aus der JavaScript-Bibliothek lodash verwenden. Die Methode heißt _.cloneDeep (obj) .

Mohamed Elshahawy
quelle
-1

Alle oben genannten Methoden behandeln nicht das tiefe Klonen von Objekten, bei denen es auf n Ebenen verschachtelt ist. Ich habe die Leistung nicht gegenüber anderen überprüft, aber sie ist kurz und einfach.

Das erste Beispiel unten zeigt Object.assigndas Klonen von Objekten, mit denen Klone bis zur ersten Ebene geklont werden.

var person = {
    name:'saksham',
    age:22,
    skills: {
        lang:'javascript',
        experience:5
    }
}

newPerson = Object.assign({},person);
newPerson.skills.lang = 'angular';
console.log(newPerson.skills.lang); //logs Angular

Mit dem folgenden Ansatz klonen tiefe Objekte

var person = {
    name:'saksham',
    age:22,
    skills: {
        lang:'javascript',
        experience:5
    }
}

anotherNewPerson = JSON.parse(JSON.stringify(person));
anotherNewPerson.skills.lang = 'angular';
console.log(person.skills.lang); //logs javascript

Saksham
quelle
JSON.parse / stringify wurde als armes tief Klonierungsverfahren erwähnt Jahren . Bitte überprüfen Sie frühere Antworten sowie verwandte Fragen. Dies ist auch nicht neu in ES6.
Dan Dascalescu
@DanDascalescu Ich weiß das und ich denke, es sollte kein Problem sein, es für einfache Objekte zu verwenden. Andere haben dies auch in ihren Antworten im selben Beitrag und sogar als Kommentare erwähnt. Ich denke, es verdient keine Ablehnung.
Saksham
Genau - "andere haben auch" JSON.parse / stringify in ihren Antworten erwähnt. Warum noch eine Antwort mit der gleichen Lösung posten?
Dan Dascalescu