Klonen eines Objekts in Node.js.

203

Was ist der beste Weg, um ein Objekt in node.js zu klonen?

zB möchte ich die Situation vermeiden, in der:

var obj1 = {x: 5, y:5};
var obj2 = obj1;
obj2.x = 6;
console.log(obj1.x); // logs 6

Das Objekt kann durchaus komplexe Typen als Attribute enthalten, sodass ein einfaches for (var x in obj1) nicht gelöst werden kann. Muss ich selbst einen rekursiven Klon schreiben oder ist etwas eingebaut, das ich nicht sehe?

schlampig
quelle
23
1. npm install underscore2. var _ = require('underscore')3 . _.clone(objToClone);
Salman von Abbas
4
Beachten Sie, dass dies im obigen Kommentar von @ SalmanPK ein flacher Klon ist. So funktioniert es für das Beispiel von slifty, aber wenn es verschachtelte Arrays oder Objekte gibt, sind sie Referenzen. : /
Jesse
1
Ich fand diesen Artikel sehr hilfreich: heyjavascript.com/4-creative-ways-to-clone-objects
Jordan Hudson
3
@ Jordan Hudson - Sehr gute Verwendung von JSON im zweiten Beispiel. var newObj = JSON.parse (JSON.stringify (oldObj)); // Jetzt ist newObj ein Klon. Das einzige Problem ist, dass stringify bei rekursiven Referenzen nicht funktioniert. Seien Sie also vorsichtig.
Kfir Erez

Antworten:

298

Möglichkeit 1

Low-Frills tiefe Kopie:

var obj2 = JSON.parse(JSON.stringify(obj1));

Möglichkeit 2 (veraltet)

Achtung: Diese Lösung ist jetzt in der Dokumentation von Node.js als veraltet markiert :

Die util._extend () -Methode sollte niemals außerhalb der internen Node.js-Module verwendet werden. Die Community hat es trotzdem gefunden und benutzt.

Es ist veraltet und sollte nicht in neuem Code verwendet werden. JavaScript verfügt über eine sehr ähnliche integrierte Funktionalität über Object.assign ().

Ursprüngliche Antwort ::

Verwenden Sie für eine flache Kopie die integrierte util._extend()Funktion von Node .

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

var obj1 = {x: 5, y:5};
var obj2 = extend({}, obj1);
obj2.x = 6;
console.log(obj1.x); // still logs 5

Der Quellcode der Node- _extendFunktion finden Sie hier: https://github.com/joyent/node/blob/master/lib/util.js

exports._extend = function(origin, add) {
  // Don't do anything if add isn't an object
  if (!add || typeof add !== 'object') return origin;

  var keys = Object.keys(add);
  var i = keys.length;
  while (i--) {
    origin[keys[i]] = add[keys[i]];
  }
  return origin;
};
Jimbo
quelle
5
Die Frage forderte speziell einen rekursiven Klon. Dies ist ein flacher Klon.
Benjamin Atkin
28
Soll ein Name nicht _*bedeuten, dass er eine private Methode ist und auf die man sich nicht verlassen sollte?
Fluffy
7
Jedes JavaScript-Projekt jeder Größe verfügt über eine oder mehrere Implementierungen von extens (), und Node ist keine Ausnahme. Der Node.js-Kern nutzt diese Funktion in großem Umfang. Um Isaacs zu zitieren: "Es wird nicht so schnell irgendwohin gehen."
Jimbo
2
hat perfekt für mich funktioniert. viel besser als mit dem Objektprototyp imo herumzuspielen
Michael Dausmann
12
Dies ist die falsche Antwort. Gemäß der Dokumentation des Knotens: nodejs.org/api/util.html#util_util_extend_obj Die util._extend()Methode sollte niemals außerhalb der internen Node.js-Module verwendet werden. Die Community hat es trotzdem gefunden und benutzt. Es ist veraltet und sollte nicht in neuem Code verwendet werden. JavaScript kommt mit sehr ähnlichen integrierten Funktionen durchObject.assign().
Jordie
264

Ich bin überrascht, dass Object.assignes nicht erwähnt wurde.

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

Falls verfügbar (z. B. Babel), können Sie den Objektverteilungsoperator verwenden :

let cloned = { ... source };
Djanowski
quelle
1
du hast meinen Tag gerettet! Danke
wzr1337
2
Dies ist eine viel bessere Lösung, als eine Bibliothek eines Drittanbieters importieren oder die unvollständige JSON-Problemumgehung verwenden zu müssen. Vielen Dank!
Neil S
75
Dies ist eine flache Kopie
Jordan Davidson
14
Warnung für Deep Clone, Für Deep Clone müssen noch andere Alternativen verwendet werden. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
gsalgadotoledo
1
Soweit ich das beurteilen kann, handelt es sich bei dem Objektverbreitungsoperator nicht um eine ES6-Sache, sondern um einen Vorschlag der Stufe 3. was bedeutet, dass Sie es mit Babel verwenden können, aber nicht ohne das, was ich verstehe. github.com/tc39/…
macdja38
24
Object.defineProperty(Object.prototype, "extend", {
    enumerable: false,
    value: function(from) {
        var props = Object.getOwnPropertyNames(from);
        var dest = this;
        props.forEach(function(name) {
            if (name in dest) {
                var destination = Object.getOwnPropertyDescriptor(from, name);
                Object.defineProperty(dest, name, destination);
            }
        });
        return this;
    }
});

Dadurch wird eine Erweiterungsmethode definiert, die Sie verwenden können. Code stammt aus diesem Artikel.

Michael Dillon
quelle
Ich sehe nicht, wie das funktionieren soll. Es ändert das ursprüngliche Objekt! Wie soll ich diese Funktion verwenden, um einen Klon eines Objekts zu erhalten? Können Sie hier einen Verwendungscode hinzufügen? Nachdem ich Ihren Beitrag und den Blog-Beitrag gelesen habe, kann ich immer noch nicht herausfinden, wie dies zum Klonen eines Objekts verwendet werden soll.
Brad
3
funktioniert das wirklich "if (name in dest)" - ändert die Eigenschaft nur, wenn sie bereits in dest vorhanden ist. es sollte negiert werden.
memical
8
Soll das Ändern von Object.prototype nicht verboten sein? Auch dieser Artikellink ist kaputt.
Daniel Schaffer
Habe gerade den Artikel-Link ausprobiert und es funktioniert bei mir. Vielleicht war es ein Netzwerkfehler, als Sie es ausprobiert haben.
Michael Dillon
Basierend auf einer Reihe von Kommentaren habe ich die Antwort so aktualisiert, dass sie eine Variante enthält, die nicht zum Prototyp des Objekts hinzugefügt wird.
Shamasis Bhattacharya
20
var obj2 = JSON.parse(JSON.stringify(obj1));
user2516109
quelle
2
Dies wurde bereits in dieser bestehenden Antwort vorgeschlagen, es macht keinen Sinn, sie zu wiederholen.
Schatten-Assistent ist Ohr für Sie
@ShadowWizard Dies sind verschiedene Methoden. Dieser konvertiert einfach zu json und zurück zum Objekt, während die verknüpfte Antwort verwendet wird Object.keys(), um durch das Objekt zu iterieren
mente
Diese Antwort ist falsch. JSON.stringify nimmt ein Datumsobjekt und reduziert es auf eine Zeichenfolge. Nach dem Parsen wird es als Zeichenfolge belassen, sodass der Status Ihres Objekts mutiert und Sie ein anderes Objekt als ursprünglich bei Datumsangaben erhalten.
17.
8

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!

Clint Harris
quelle
7

Dieser Code funktioniert auch, weil die Object.create () -Methode ein neues Objekt mit dem angegebenen Prototypobjekt und den angegebenen Eigenschaften erstellt.

var obj1 = {x:5, y:5};

var obj2 = Object.create(obj1);

obj2.x; //5
obj2.x = 6;
obj2.x; //6

obj1.x; //5
Hiron
quelle
4
Dies ist eine flache Kopie
Radagast the Brown
6

Der einfache und schnellste Weg, ein Objekt in NodeJS zu klonen, ist die Verwendung der Object.keys (obj) -Methode

var a = {"a": "a11", "b": "avc"};
var b;

for(var keys = Object.keys(a), l = keys.length; l; --l)
{
   b[ keys[l-1] ] = a[ keys[l-1] ];
}
b.a = 0;

console.log("a: " + JSON.stringify(a)); // LOG: a: {"a":"a11","b":"avc"} 
console.log("b: " + JSON.stringify(b)); // LOG: b: {"a":0,"b":"avc"}

Die Methode Object.keys erfordert JavaScript 1.8.5; nodeJS v0.4.11 unterstützt diese Methode

Aber natürlich müssen für verschachtelte Objekte rekursive Funktionen implementiert werden


Eine andere Lösung ist die Verwendung von nativem JSON (implementiert in JavaScript 1.7), aber es ist viel langsamer (~ 10-mal langsamer) als das vorherige

var a = {"a": i, "b": i*i};
var b = JSON.parse(JSON.stringify(a));
b.a = 0;
Nihil
quelle
5

Es gibt auch ein Projekt auf Github, das darauf abzielt, ein direkterer Hafen der jQuery.extend():

https://github.com/dreamerslab/node.extend

Ein Beispiel, das aus den jQuery-Dokumenten geändert wurde :

var extend = require('node.extend');

var object1 = {
    apple: 0,
    banana: {
        weight: 52,
        price: 100
    },
    cherry: 97
};

var object2 = {
    banana: {
        price: 200
    },
    durian: 100
};

var merged = extend(object1, object2);
Geil
quelle
3

Auf der Suche nach einer echten Klonoption bin ich über Ridcullys Link hierher gestolpert:

http://my.opera.com/GreyWyvern/blog/show.dml/1725165

Ich habe die Lösung auf dieser Seite so geändert, dass die an den ObjectPrototyp angehängte Funktion nicht aufzählbar ist. Hier ist mein Ergebnis:

Object.defineProperty(Object.prototype, 'clone', {
    enumerable: false,
    value: function() {
        var newObj = (this instanceof Array) ? [] : {};
        for (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;
    }
});

Hoffentlich hilft das auch jemand anderem. Beachten Sie, dass es einige Einschränkungen gibt ... insbesondere bei Eigenschaften mit dem Namen "Klon". Das funktioniert gut für mich. Ich nehme keine Anerkennung dafür, dass ich es geschrieben habe. Wieder habe ich nur geändert, wie es definiert wurde.

Brad
quelle
Das ist falsch. Der Datumstyp ist ein Objekt, daher würde dieser Code Datumsangaben durch leere Objekte ersetzen. Verwenden Sie dies nicht.
Jtblin
3

Sie leiden, aber die Lösung ist einfach.

var obj1 = {x: 5, y:5};

var obj2 = {...obj1}; // Boom

Ngend Lio
quelle
0

Wenn Sie ein Kaffeeskript verwenden, ist dies so einfach wie:

newObject = {}
newObject[key] = value  for own key,value of oldObject

Obwohl dies kein tiefer Klon ist.

Balupton
quelle
0

Keine der Antworten hat mich zufrieden gestellt, einige funktionieren nicht oder sind nur flache Klone. Die Antworten von @ clint-harris und die Verwendung von JSON.parse / stringify sind gut, aber ziemlich langsam. Ich habe ein Modul gefunden, das Deep Cloning schnell ausführt : https://github.com/AlexeyKupershtokh/node-v8-clone

jtblin
quelle
0

Es gibt keine integrierte Möglichkeit, einen echten Klon (Deep Copy) eines Objekts in node.js zu erstellen. Es gibt einige knifflige Randfälle, daher sollten Sie auf jeden Fall eine Bibliothek dafür verwenden. Ich habe eine solche Funktion für meine simpleoo- Bibliothek geschrieben. Sie können die deepCopyFunktion verwenden, ohne etwas anderes aus der Bibliothek zu verwenden (was ziemlich klein ist), wenn Sie es nicht benötigen. Diese Funktion unterstützt das Klonen mehrerer Datentypen, einschließlich Arrays, Datumsangaben und regulärer Ausdrücke, unterstützt rekursive Referenzen und funktioniert auch mit Objekten, für deren Konstruktorfunktionen Parameter erforderlich sind.

Hier ist der Code:

//If Object.create isn't already defined, we just do the simple shim, without the second argument,
//since that's all we need here
var object_create = Object.create;
if (typeof object_create !== 'function') {
    object_create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

/**
 * Deep copy an object (make copies of all its object properties, sub-properties, etc.)
 * An improved version of http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
 * that doesn't break if the constructor has required parameters
 * 
 * It also borrows some code from http://stackoverflow.com/a/11621004/560114
 */ 
function deepCopy = function deepCopy(src, /* INTERNAL */ _visited) {
    if(src == null || typeof(src) !== 'object'){
        return src;
    }

    // Initialize the visited objects array if needed
    // This is used to detect cyclic references
    if (_visited == undefined){
        _visited = [];
    }
    // Ensure src has not already been visited
    else {
        var i, len = _visited.length;
        for (i = 0; i < len; i++) {
            // If src was already visited, don't try to copy it, just return the reference
            if (src === _visited[i]) {
                return src;
            }
        }
    }

    // Add this object to the visited array
    _visited.push(src);

    //Honor native/custom clone methods
    if(typeof src.clone == 'function'){
        return src.clone(true);
    }

    //Special cases:
    //Array
    if (Object.prototype.toString.call(src) == '[object Array]') {
        //[].slice(0) would soft clone
        ret = src.slice();
        var i = ret.length;
        while (i--){
            ret[i] = deepCopy(ret[i], _visited);
        }
        return ret;
    }
    //Date
    if (src instanceof Date) {
        return new Date(src.getTime());
    }
    //RegExp
    if (src instanceof RegExp) {
        return new RegExp(src);
    }
    //DOM Element
    if (src.nodeType && typeof src.cloneNode == 'function') {
        return src.cloneNode(true);
    }

    //If we've reached here, we have a regular object, array, or function

    //make sure the returned object has the same prototype as the original
    var proto = (Object.getPrototypeOf ? Object.getPrototypeOf(src): src.__proto__);
    if (!proto) {
        proto = src.constructor.prototype; //this line would probably only be reached by very old browsers 
    }
    var ret = object_create(proto);

    for(var key in src){
        //Note: this does NOT preserve ES5 property attributes like 'writable', 'enumerable', etc.
        //For an example of how this could be modified to do so, see the singleMixin() function
        ret[key] = deepCopy(src[key], _visited);
    }
    return ret;
};
Matt Browne
quelle
0
npm install node-v8-clone

Der schnellste Kloner öffnet die native Klonmethode aus node.js.

var clone = require('node-v8-clone').clone;
var newObj = clone(obj, true); //true - deep recursive clone
Oleksiy Chechel
quelle
0

Eine andere Lösung besteht darin, direkt in die neue Variable zu kapseln, indem Sie Folgendes verwenden: obj1= {...obj2}

labelle
quelle
Dies ist eine flache Kopie
Rémi Doolaeghe
0

Sie können diese Klonbibliothek auch zum tiefen Klonen von Objekten verwenden.

 npm install --save clone
const clone = require('clone');

const clonedObject = clone(sourceObject);
Lanil Marasinghe
quelle
-2

Sie können Objekte prototypisieren und dann jedes Mal eine Objektinstanz aufrufen, wenn Sie ein Objekt verwenden und ändern möchten:

function object () {
  this.x = 5;
  this.y = 5;
}
var obj1 = new object();
var obj2 = new object();
obj2.x = 6;
console.log(obj1.x); //logs 5

Sie können Argumente auch an den Objektkonstruktor übergeben

function object (x, y) {
   this.x = x;
   this.y = y;
}
var obj1 = new object(5, 5);
var obj2 = new object(6, 6);
console.log(obj1.x); //logs 5
console.log(obj2.x); //logs 6

Hoffe das ist hilfreich.

user3459287
quelle