Clonazione di un oggetto in Node.js


203

Qual è il modo migliore per clonare un oggetto in node.js

ad esempio, voglio evitare la situazione in cui:

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

L'oggetto può contenere tipi complessi come attributi, quindi un semplice per (var x in obj1) non si risolve. Devo scrivere io stesso un clone ricorsivo o c'è qualcosa incorporato che non vedo?


23
1. npm install underscore2. var _ = require('underscore')3 _.clone(objToClone).;
Salman von Abbas,

4
Si noti che nel commento di @ SalmanPK sopra, questo è un clone superficiale . quindi funzionerà per l'esempio di slifty, ma se ci sono matrici o oggetti nidificati, saranno riferimenti. : /
Jesse il

1
Ho trovato questo articolo molto utile: heyjavascript.com/4-creative-ways-to-clone-objects
Jordan Hudson

3
@Jordan Hudson - Molto bello l'uso di JSON nel secondo esempio. var newObj = JSON.parse (JSON.stringify (oldObj)); // Ora newObj è un clone. L'unico problema è che stringify non funzionerà su riferimenti ricorsivi, quindi è necessario fare attenzione.
Kfir Erez,

Risposte:


298

Possibilità 1

Copia profonda senza fronzoli:

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

Possibilità 2 (obsoleto)

Attenzione: questa soluzione è ora contrassegnata come obsoleta in documentazione di Node.js :

Il metodo util._extend () non è mai stato progettato per essere utilizzato al di fuori dei moduli interni Node.js. La community l'ha trovata e utilizzata comunque.

È obsoleto e non deve essere utilizzato nel nuovo codice. JavaScript è dotato di funzionalità incorporate molto simili tramite Object.assign ().

Risposta originale: ::

Per una copia superficiale, utilizzare la funzione integrata di Node util._extend().

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

Il codice sorgente della _extendfunzione del nodo è qui: 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;
};

5
La domanda chiedeva specificamente un clone ricorsivo. Questo è un clone superficiale.
Benjamin Atkin,

28
Un nome come non _*dovrebbe significare che è un metodo privato e su cui non fare affidamento?
Fluffy,

7
Ogni progetto JavaScript di qualsiasi dimensione ha una o più implementazioni di extender () e Nodo non fa eccezione. Il core Node.js fa ampio uso di questa funzione. Per citare Isaacs, "Presto non andrà da nessuna parte."
Jim

2
ha funzionato perfettamente per me. molto meglio che scherzare con Object prototype imo
Michael Dausmann,

12
Questa è la risposta ERRATA. Come da documentazione del nodo: nodejs.org/api/util.html#util_util_extend_obj Il util._extend()metodo non è mai stato progettato per essere utilizzato al di fuori dei moduli interni Node.js. La community l'ha trovata e utilizzata comunque. È obsoleto e non deve essere utilizzato nel nuovo codice. JavaScript è dotato di funzionalità incorporate molto simili attraversoObject.assign().
Jordie,

265

Sono sorpreso che Object.assignnon sia stato menzionato.

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

Se disponibile (ad esempio Babel), è possibile utilizzare l' operatore diffusione oggetto :

let cloned = { ... source };

1
mi hai salvato la giornata! Grazie
wzr1337,

2
questa è una soluzione molto migliore rispetto a dover importare una libreria di terze parti o utilizzare la soluzione JSON imperfetta. Grazie!
Neil S

75
questa è una copia superficiale
Jordan Davidson,

14
Avvertenza per Deep Clone, per deep clone è ancora necessario utilizzare altre alternative. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
gsalgadotoledo

1
Da quello che posso dire, l'operatore di diffusione degli oggetti non è una cosa ES6 ma una proposta di fase 3. il che significa che puoi usarlo con Babel ma non senza quello che ho capito. 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;
    }
});

Ciò definirà un metodo di estensione che è possibile utilizzare. Il codice proviene da questo articolo.


Non vedo come dovrebbe funzionare. Modifica l'oggetto originale! Come dovrei usare questa funzione per ottenere un clone di un oggetto? Puoi aggiungere un codice di utilizzo qui? Dopo aver letto il tuo post e il post sul blog, non riesco ancora a capire come questo debba essere usato per clonare un oggetto.
Brad

3
funziona davvero? "if (name in dest)" - cambierà la proprietà solo se esiste già in dest. dovrebbe essere negato.
memical

8
La modifica di Object.prototype non dovrebbe essere verboten? Anche quel collegamento all'articolo è interrotto.
Daniel Schaffer,

Ho appena provato il collegamento all'articolo e funziona per me. Forse è stato un blip di rete quando l'hai provato.
Michael Dillon,

Sulla base di una serie di commenti, ho aggiornato la risposta per includere una variante che non si aggiunge al prototipo dell'oggetto.
Shamasis Bhattacharya,

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

2
Questo è già stato suggerito in questa risposta esistente, inutile ripeterlo.
Shadow Wizard is Ear For You

@ShadowWizard questi sono metodi diversi. Questo semplicemente si converte in json e poi di nuovo in oggetto, mentre la risposta collegata usa Object.keys()per scorrere l'
argomento

Questa risposta è sbagliata JSON.stringify accetta un oggetto data e lo riduce in una stringa, quindi dopo averlo analizzato lo lascia come una stringa in modo da mutare lo stato dell'oggetto lasciandoti con un oggetto diverso rispetto all'inizio nel caso delle date.
twboc



8

Ci sono alcuni moduli Node là fuori se non vuoi "rollare il tuo". Questo sembra buono: https://www.npmjs.com/package/clone

Sembra che gestisce tutti i tipi di cose, compresi i riferimenti circolari. Dalla pagina github :

master clone clonazione di oggetti, matrici, oggetti Date e oggetti RegEx. Tutto viene clonato in modo ricorsivo, in modo da poter clonare le date in matrici in oggetti, ad esempio. [...] Riferimenti circolari? Sì!


7

Questo codice funziona anche perché il metodo Object.create () crea un nuovo oggetto con l'oggetto prototipo e le proprietà specificati.

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

var obj2 = Object.create(obj1);

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

obj1.x; //5

4
questa è una copia superficiale
Radagast the Brown

6

Il modo più semplice e veloce per clonare un oggetto in NodeJS è utilizzare il metodo Object.keys (obj)

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"}

Il metodo Object.keys richiede JavaScript 1.8.5; nodeJS v0.4.11 supporta questo metodo

ma ovviamente per gli oggetti nidificati è necessario implementare funzioni ricorsive


Un'altra soluzione consiste nell'utilizzare JSON nativo (implementato in JavaScript 1.7), ma è molto più lento (~ 10 volte più lento) rispetto al precedente

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

5

Esiste anche un progetto su Github che mira ad essere un porto più diretto di jQuery.extend():

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

Un esempio, modificato dai documenti jQuery :

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);


4

Soffrite tutti eppure la soluzione è semplice.

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

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


3

Alla ricerca di una vera opzione di clone, mi sono imbattuto nel link di ridcully a qui:

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

Ho modificato la soluzione in quella pagina in modo che la funzione allegata al Objectprototipo non sia enumerabile. Ecco il mio risultato:

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;
    }
});

Spero che questo aiuti anche qualcun altro. Nota che ci sono alcuni avvertimenti ... in particolare con proprietà denominate "clone". Questo funziona bene per me. Non mi merito per averlo scritto. Ancora una volta, ho cambiato solo il modo in cui veniva definito.


Questo è sbagliato. Il tipo di date è oggetto, quindi questo codice sostituirà le date con oggetti vuoti ... Non usare questo.
jtblin,


0

Se stai usando la sceneggiatura del caffè, è facile come:

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

Anche se questo non è un clone profondo.


0

Nessuna delle risposte mi ha soddisfatto, molte non funzionano o sono solo cloni superficiali, le risposte di @ clint-harris e l'uso di JSON.parse / stringify sono buone ma piuttosto lente. Ho trovato un modulo che esegue velocemente la clonazione profonda: https://github.com/AlexeyKupershtokh/node-v8-clone


0

Non esiste un modo integrato per eseguire un vero clone (copia profonda) di un oggetto in node.js. Ci sono alcuni casi difficili quindi dovresti assolutamente usare una libreria per questo. Ho scritto una tale funzione per la mia libreria simpleoo . Puoi usare ildeepCopy funzione senza usare nient'altro dalla libreria (che è piuttosto piccola) se non ti serve. Questa funzione supporta la clonazione di più tipi di dati, inclusi array, date ed espressioni regolari, supporta riferimenti ricorsivi e funziona anche con oggetti le cui funzioni di costruzione hanno parametri richiesti.

Ecco il codice:

//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;
};

0
npm install node-v8-clone

Cloner più veloce, apre il metodo clone nativo da node.js

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

0

Un'altra soluzione è incapsulare direttamente nella nuova variabile usando: obj1= {...obj2}


Questa è una copia superficiale
Rémi Doolaeghe,

0

Puoi anche usare questa libreria di cloni per clonare in profondità oggetti.

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

const clonedObject = clone(sourceObject);

-2

È possibile prototipare l'oggetto e quindi chiamare l'istanza dell'oggetto ogni volta che si desidera utilizzare e modificare l'oggetto:

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

Puoi anche passare argomenti al costruttore di oggetti

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

Spero sia utile.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.