Nodejs: So klonen Sie ein Objekt


89

Wenn ich ein Array klone, verwende ich cloneArr = arr.slice()

Ich möchte wissen, wie man ein Objekt in nodejs klont.

Antworten:


173

Bei Dienstprogrammen und Klassen, bei denen nicht jeder Leistungsabfall beeinträchtigt werden muss, betrüge ich häufig und verwende JSON, um eine tiefe Kopie zu erstellen:

function clone(a) {
   return JSON.parse(JSON.stringify(a));
}

Dies ist nicht die einzige oder eleganteste Antwort. Alle anderen Antworten sollten bei Produktionsengpässen berücksichtigt werden. Dies ist jedoch eine schnelle und schmutzige Lösung, sehr effektiv und in den meisten Situationen nützlich, in denen ich einen einfachen Hash von Eigenschaften klonen würde.


2
@ Djechlin Sicher tut es. Probieren Sie es aus: jsfiddle.net/katowulf/E5jC3 (getestet mit Knoten 0.10.11) Es wird nicht in der Lage sein, Funktionen oder prototypische Daten wiederherzustellen, aber die Werte werden in Ordnung gebracht.
Kato

11
Dies wird Daten in Zeichenfolgen konvertieren
backus

4
@Backus sowie Objekte und Funktionen.
Kato

2
@Kato, warum sollten Zirkelverweise bedeuten, dass keine tiefen Kopien erstellt werden müssen? Das sind zwei Dinge, die nichts miteinander zu tun haben. Es ist sicherlich möglich, eine gute Klonmethode für NodeJS zu schreiben, die Zirkelverweise unterstützt und nicht auf die Verwendung von JSON zum Klonen zurückgreift. github.com/pvorb/node-clone
Samuel Neff

2
Bereits in den Kommentaren und in der Beschreibung vermerkt. Sie können keine Funktion, keinen Objektprototyp oder andere Dinge klonen, die Sie mit einer generischen Klonmethode nicht versuchen sollten. Sie benötigen mehr als eine Codezeile dafür und sollten diesen Müll wahrscheinlich sowieso nicht klonen - fügen Sie Ihrer Klasse eine Klonmethode hinzu, die weiß, wie man mit den Interna umgeht und wie man das machtnewObj = obj.clone(args...);
Kato

33

Object.assign wurde in keiner der obigen Antworten erwähnt.

let cloned = Object.assign({}, source);

Wenn Sie mit ES6 arbeiten, können Sie den Spread-Operator verwenden:

let cloned = { ... source };

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


27
Beachten Sie, dass beide flache Klone sind und der Spread-Operator für Objekte node.js 8+ benötigt.
16.

1
Wie @jlh erwähnt hat, funktioniert dies nicht für verschachtelte Objekte. Weitere
Informationen

Wie Shnd sagte, ist diese Antwort falsch. Für das Deep Cloning müssen andere Alternativen verwendet werden, da Object.assign () Eigenschaftswerte kopiert. Wenn der Quellwert eine Referenz auf ein Objekt ist, kopiert er nur diesen Referenzwert.
Troy

1
Dies klont nicht, es verweist nur auf das ursprüngliche Objekt, was bedeutet, wenn Sie etwas ändern, geht es an den Meister
Aidan Welch

@AidanWelch Ich denke nicht, dass das richtig ist. let a = {x:1}; let b = Object.assign({}, a); a.x = 2; a.y = 1; console.log(a, b);
Miguel Pynto

20

Es gibt einige Node-Module, wenn Sie nicht "Ihre eigenen rollen" möchten. Dieser sieht gut aus: https://www.npmjs.com/package/clone

Sieht so aus, als würde es alle möglichen Dinge behandeln, einschließlich Zirkelverweise. Von der Github- Seite:

Klonmeister klonen Objekte, Arrays, Datumsobjekte und RegEx-Objekte. Alles wird rekursiv geklont, sodass Sie beispielsweise Datumsangaben in Arrays in Objekten klonen können. [...] Rundschreiben? Ja!


10

Es ist schwierig, eine generische, aber nützliche Klonoperation durchzuführen, da es davon abhängt, wie das bestimmte Objekt funktionieren soll, was rekursiv geklont und was nur kopiert werden soll.

Etwas, das nützlich sein kann, ist

function clone(x)
{
    if (x === null || x === undefined)
        return x;
    if (typeof x.clone === "function")
        return x.clone();
    if (x.constructor == Array)
    {
        var r = [];
        for (var i=0,n=x.length; i<n; i++)
            r.push(clone(x[i]));
        return r;
    }
    return x;
}

In diesem Code ist die Logik

  • im Falle von nulloder undefinedeinfach nur zurückgeben (der Sonderfall wird benötigt, weil es ein Fehler ist, zu versuchen, festzustellen, ob eine cloneMethode vorhanden ist)
  • Hat das Objekt eine cloneMethode? dann benutze das
  • Ist das Objekt ein Array? Führen Sie dann eine rekursive Klonoperation durch
  • Andernfalls geben Sie einfach den gleichen Wert zurück

Diese Klonfunktion sollte es ermöglichen, benutzerdefinierte Klonmethoden einfach zu implementieren ... zum Beispiel

function Point(x, y)
{
    this.x = x;
    this.y = y;

    ...
}

Point.prototype.clone = function()
{
    return new Point(this.x, this.y);
};



function Polygon(points, style)
{
    this.points = points;
    this.style = style;

    ...
}

Polygon.prototype.clone = function()
{
    return new Polygon(clone(this.points),
                       this.style);
};

Wenn Sie im Objekt wissen, dass ein korrekter Klonvorgang für ein bestimmtes Array nur eine flache Kopie ist, können Sie values.slice()stattdessen aufrufen clone(values).

Zum Beispiel fordere ich im obigen Code ausdrücklich, dass beim Klonen eines Polygonobjekts die Punkte geklont werden, aber dasselbe Stilobjekt verwendet wird. Wenn ich stattdessen auch das Stilobjekt klonen möchte, kann ich einfach übergeben clone(this.style).


1
+1 für die Erwähnung, dass Objekte die .cloneMethode selbst implementieren müssen. Dies ist der beste Weg, um mit dem Klonen von Objekten umzugehen.
Raynos

3
if (x.clone)sollte seinif (typeof x.clone === 'function')
Yanick Rochon

@ YanickRochon: Danke, behoben. Entschuldigung, irgendwie habe ich diesen Kommentar vorher nicht bemerkt ...
6502

9

Es gibt keine native Methode zum Klonen von Objekten. Unterstrichen implementiert, _.clonewas ein flacher Klon ist.

_.clone = function(obj) {
  return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};

Es schneidet es entweder oder erweitert es.

Hier ist _.extend

// extend the obj (first parameter)
_.extend = function(obj) {
  // for each other parameter
  each(slice.call(arguments, 1), function(source) {
    // loop through all properties of the other objects
    for (var prop in source) {
      // if the property is not undefined then add it to the object.
      if (source[prop] !== void 0) obj[prop] = source[prop];
    }
  });
  // return the object (first parameter)
  return obj;
};

Extend durchläuft einfach alle Elemente und erstellt ein neues Objekt mit den darin enthaltenen Elementen.

Sie können Ihre eigene naive Implementierung einführen, wenn Sie möchten

function clone(o) {
  var ret = {};
  Object.keys(o).forEach(function (val) {
    ret[val] = o[val];
  });
  return ret;
}

Es gibt gute Gründe, tiefes Klonen zu vermeiden, da Verschlüsse nicht geklont werden können.

Ich habe persönlich eine Frage gestellt deep cloning objects beforeund bin zu dem Schluss gekommen, dass Sie es einfach nicht tun.

Meine Empfehlung ist die Verwendung underscoreund die _.cloneMethode für flache Klone


9

Für eine flache Kopie verwende ich gerne das Verkleinerungsmuster (normalerweise in einem Modul oder ähnlichem) wie folgt:

var newObject = Object.keys(original).reduce(function (obj, item) {
    obj[item] = original[item];
    return obj;
},{});

Hier ist ein jsperf für einige der Optionen: http://jsperf.com/shallow-copying


einfach, elegant, brillant. Ich freue mich auf Ihre weiteren SO-Beiträge ^ _ ^
Vielen Dank,

7

Alte Frage, aber es gibt eine elegantere Antwort als bisher vorgeschlagen; Verwenden Sie die integrierten utils._extend:

var extend = require("util")._extend;

var varToCopy = { test: 12345, nested: { val: 6789 } };

var copiedObject = extend({}, varToCopy);

console.log(copiedObject);

// outputs:
// { test: 12345, nested: { val: 6789 } }

Beachten Sie die Verwendung des ersten Parameters mit einem leeren Objekt {} - dies weist darauf hin, dass die kopierten Objekte in ein neues Objekt kopiert werden müssen. Wenn Sie ein vorhandenes Objekt als ersten Parameter verwenden, werden die zweiten (und alle nachfolgenden) Parameter über die erste Parametervariable tief zusammengeführt.

Mit den obigen Beispielvariablen können Sie auch Folgendes tun:

var anotherMergeVar = { foo: "bar" };

extend(copiedObject, { anotherParam: 'value' }, anotherMergeVar);

console.log(copiedObject);

// outputs:
// { test: 12345, nested: { val: 6789 }, anotherParam: 'value', foo: 'bar' }

Sehr praktisches Dienstprogramm, insbesondere dort, wo ich es gewohnt bin, in AngularJS und jQuery zu erweitern.

Hoffe das hilft jemand anderem; Überschreiben von Objektreferenzen sind ein Elend, und dies löst es jedes Mal!


1
Die Verwendung von _extend wird jetzt abgeschrieben. Verwenden Sie stattdessen Object.assign (): developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
MacroMan

7

Sie können auch lodash verwenden . Es hat einen Klon und cloneDeep Methoden.

var _= require('lodash');

var objects = [{ 'a': 1 }, { 'b': 2 }];

var shallow = _.clone(objects);
console.log(shallow[0] === objects[0]);
// => true

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);

1

Je nachdem, was Sie mit Ihrem geklonten Objekt tun möchten, können Sie den prototypischen Vererbungsmechanismus von Javascript verwenden und ein etwas geklontes Objekt erzielen durch:

var clonedObject = Object.create(originalObject);

Denken Sie daran, dass dies kein vollständiger Klon ist - zum Guten oder Schlechten.

Eine gute Sache dabei ist, dass Sie das Objekt tatsächlich nicht dupliziert haben, sodass der Speicherbedarf gering ist.

Einige schwierige Dinge, die Sie bei dieser Methode beachten sollten, sind, dass die Iteration der in der Prototypenkette definierten Eigenschaften manchmal etwas anders funktioniert und dass Änderungen am ursprünglichen Objekt auch das geklonte Objekt betreffen, sofern diese Eigenschaft nicht auch für sich selbst festgelegt wurde .


3
Das ist so lächerlich gefährlich und überhaupt nicht wie ein Klon. var a = {foo: "bar"}, b = Object.create(a); a.foo = "broken"; console.log(b.foo); // "broken";
Danke

@naomik: b.foo = "working"; console.log(a.foo); // still "broken";Man muss sich natürlich bewusst sein, dass Änderungen am Originalobjekt im "Klon" wiedergegeben werden und dass Änderungen am "Klon" nicht im Originalobjekt wiedergegeben werden - aber ich würde es nicht als gefährlich bezeichnen - Es hängt alles davon ab, was Sie mit Ihrem Klon machen wollen
VoxPelli

@naomik: Ich bin ziemlich neu bei JS, daher möchte ich einige Präzisionen. Der von olli-k vorgeschlagene flache Klon, den Sie mochten, scheint (IMHO, aber ich könnte mich irren) dieselben Einschränkungen zu haben wie die von voxpelli vorgeschlagene Lösung. Das Original und das flache Schließen teilen sich auch die gleichen "Elemente". Das Aktualisieren eines Elements im Original wirkt sich auch auf den flachen Klon aus. Oder?
Bmorin

@bmornin, wenn Sie die Technik von Olli K verwenden, führt das Ändern des ursprünglichen Objekts nicht zu einer Änderung des neuen Objekts.
Danke

1
@VoxPelli Der ganze Zweck eines „Klon“ ist zu isolieren Änderungen aus dem ursprünglichen Objekt, daher ist diese Methode ist gefährlicher.
Doug

1

Ich habe eine vollständige, tiefe Kopie implementiert. Ich glaube, es ist die beste Wahl für eine generische Klonmethode, aber es werden keine zyklischen Referenzen verarbeitet.

Anwendungsbeispiel:

parent = {'prop_chain':3}
obj = Object.create(parent)
obj.a=0; obj.b=1; obj.c=2;

obj2 = copy(obj)

console.log(obj, obj.prop_chain)
// '{'a':0, 'b':1, 'c':2} 3
console.log(obj2, obj2.prop_chain)
// '{'a':0, 'b':1, 'c':2} 3

parent.prop_chain=4
obj2.a = 15

console.log(obj, obj.prop_chain)
// '{'a':0, 'b':1, 'c':2} 4
console.log(obj2, obj2.prop_chain)
// '{'a':15, 'b':1, 'c':2} 4

Der Code selbst:

Dieser Code kopiert Objekte mit ihren Prototypen und kopiert auch Funktionen (kann für jemanden nützlich sein).

function copy(obj) {
  // (F.prototype will hold the object prototype chain)
  function F() {}
  var newObj;

  if(typeof obj.clone === 'function')
    return obj.clone()

  // To copy something that is not an object, just return it:
  if(typeof obj !== 'object' && typeof obj !== 'function' || obj == null)
    return obj;

  if(typeof obj === 'object') {    
    // Copy the prototype:
    newObj = {}
    var proto = Object.getPrototypeOf(obj)
    Object.setPrototypeOf(newObj, proto)
  } else {
    // If the object is a function the function evaluate it:
    var aux
    newObj = eval('aux='+obj.toString())
    // And copy the prototype:
    newObj.prototype = obj.prototype
  }

  // Copy the object normal properties with a deep copy:
  for(var i in obj) {
    if(obj.hasOwnProperty(i)) {
      if(typeof obj[i] !== 'object')
        newObj[i] = obj[i]
      else
        newObj[i] = copy(obj[i])
    }
  }

  return newObj;
}

Mit dieser Kopie kann ich keinen Unterschied zwischen dem Original und dem kopierten finden, außer wenn das Original Verschlüsse für seine Konstruktion verwendet hat, daher denke ich, dass es eine gute Implementierung ist.

Ich hoffe, es hilft


1

für Array kann man verwenden

var arr = [1,2,3]; 
var arr_2 = arr ; 

print ( arr_2 ); 

arr = arr.slice (0);

print ( arr ); 

arr[1]=9999; 

print ( arr_2 ); 

1

Objekte und Arrays in JavaScript verwenden den Aufruf als Referenz. Wenn Sie den kopierten Wert aktualisieren, wird dies möglicherweise auf das ursprüngliche Objekt angewendet. Um dies zu verhindern, können Sie das Objekt mit dem Befehl run der Methode cloneDeep der lodash-Bibliothek tief klonen und verhindern, dass die Referenz übergeben wird

npm install lodash

const ld = require('lodash')
const objectToCopy = {name: "john", age: 24}
const clonedObject = ld.cloneDeep(objectToCopy)
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.