Come clonare in profondità in javascript


105

Come si clona in profondità un oggetto Javascript?

So che ci sono varie funzioni basate su framework come JSON.parse(JSON.stringify(o))e $.extend(true, {}, o)ma non voglio usare un framework come quello.

Qual è il modo più elegante o efficiente per creare un clone profondo.

Ci preoccupiamo per casi limite come la clonazione di array. Non rompere le catene di prototipi, occuparsi dell'autoreferenzialità.

Non ci interessa supportare la copia di oggetti DOM e simili perché .cloneNodeesiste per questo motivo.

Poiché voglio principalmente utilizzare cloni profondi node.jsnell'utilizzo delle funzionalità ES5 del motore V8 è accettabile.

[Modificare]

Prima che qualcuno mi suggerisca di menzionare, c'è una netta differenza tra la creazione di una copia ereditando prototipicamente dall'oggetto e la clonazione . Il primo rovina la catena dei prototipi.

[Ulteriore modifica]

Dopo aver letto la tua risposta sono arrivato alla fastidiosa scoperta che la clonazione di interi oggetti è un gioco molto pericoloso e difficile. Prendiamo ad esempio il seguente oggetto basato sulla chiusura

var o = (function() {
     var magic = 42;

     var magicContainer = function() {
          this.get = function() { return magic; };
          this.set = function(i) { magic = i; };
     }

      return new magicContainer;
}());

var n = clone(o); // how to implement clone to support closures

Esiste un modo per scrivere una funzione clone che clona l'oggetto, ha lo stesso stato al momento della clonazione ma non può alterare lo stato di osenza scrivere un parser JS in JS.

Non dovrebbe più esserci bisogno nel mondo reale di tale funzione. Questo è mero interesse accademico.


2
Prima che venga contrassegnato come duplicato, ho esaminato stackoverflow.com/questions/122102/… e non ho trovato alcuna risposta che trattasse tutti i casi limite.
Raynos

Il requisito nella sezione "ulteriori modifiche" è impossibile da ottenere senza l '"aiuto" dell'oggetto stesso, poiché tali variabili private sono veramente private e di conseguenza non accessibili da una clonefunzione generica . L'oggetto in questione dovrebbe esporre il proprio clonemetodo su misura .
trincot

Ne ho letto un sacco stasera e tra le risorse che ho trovato c'era questo post sul blog dall'aspetto brutto che include un paio di hack per accedere all'algoritmo di clone strutturato nel browser: dassur.ma/things/deep-copy
Cat il

Risposte:


67

Dipende davvero da cosa vorresti clonare. Si tratta di un vero oggetto JSON o di un qualsiasi oggetto in JavaScript? Se vuoi fare un clone, potresti metterti nei guai. Quale guaio? Lo spiegherò di seguito, ma prima, un esempio di codice che clona i letterali oggetto, eventuali primitive, array e nodi DOM.

function clone(item) {
    if (!item) { return item; } // null, undefined values check

    var types = [ Number, String, Boolean ], 
        result;

    // normalizing primitives if someone did new String('aaa'), or new Number('444');
    types.forEach(function(type) {
        if (item instanceof type) {
            result = type( item );
        }
    });

    if (typeof result == "undefined") {
        if (Object.prototype.toString.call( item ) === "[object Array]") {
            result = [];
            item.forEach(function(child, index, array) { 
                result[index] = clone( child );
            });
        } else if (typeof item == "object") {
            // testing that this is DOM
            if (item.nodeType && typeof item.cloneNode == "function") {
                result = item.cloneNode( true );    
            } else if (!item.prototype) { // check that this is a literal
                if (item instanceof Date) {
                    result = new Date(item);
                } else {
                    // it is an object literal
                    result = {};
                    for (var i in item) {
                        result[i] = clone( item[i] );
                    }
                }
            } else {
                // depending what you would like here,
                // just keep the reference, or create new object
                if (false && item.constructor) {
                    // would not advice to do that, reason? Read below
                    result = new item.constructor();
                } else {
                    result = item;
                }
            }
        } else {
            result = item;
        }
    }

    return result;
}

var copy = clone({
    one : {
        'one-one' : new String("hello"),
        'one-two' : [
            "one", "two", true, "four"
        ]
    },
    two : document.createElement("div"),
    three : [
        {
            name : "three-one",
            number : new Number("100"),
            obj : new function() {
                this.name = "Object test";
            }   
        }
    ]
})

E ora, parliamo dei problemi che potresti incontrare quando inizi a clonare oggetti REALI. Sto parlando ora di oggetti che crei facendo qualcosa di simile

var User = function(){}
var newuser = new User();

Ovviamente puoi clonarli, non è un problema, ogni oggetto espone la proprietà del costruttore e puoi usarlo per clonare oggetti, ma non sempre funzionerà. Puoi anche fare semplici for insu questi oggetti, ma va nella stessa direzione: guai. Ho anche incluso la funzionalità di clonazione all'interno del codice, ma è esclusa if( false )dall'istruzione.

Allora, perché la clonazione può essere un problema? Bene, prima di tutto, ogni oggetto / istanza potrebbe avere uno stato. Non puoi mai essere sicuro che i tuoi oggetti non abbiano ad esempio variabili private, e se questo è il caso, clonando l'oggetto, interrompi semplicemente lo stato.

Immagina che non ci sia uno stato, va bene. Allora abbiamo ancora un altro problema. La clonazione tramite il metodo "costruttore" ci darà un altro ostacolo. È una dipendenza dagli argomenti. Non si può mai essere sicuri che qualcuno che ha creato questo oggetto, non l'abbia fatto, in qualche modo

new User({
   bike : someBikeInstance
});

Se questo è il caso, sei sfortunato, probabilmente someBikeInstance è stata creata in qualche contesto e quel contesto non è noto per il metodo di clonazione.

Quindi che si fa? Puoi ancora fare una for insoluzione e trattare tali oggetti come normali oggetti letterali, ma forse è un'idea non clonare affatto tali oggetti e passare semplicemente il riferimento a questo oggetto?

Un'altra soluzione è: potresti impostare una convenzione per cui tutti gli oggetti che devono essere clonati dovrebbero implementare questa parte da soli e fornire un metodo API appropriato (come cloneObject). Qualcosa che cloneNodesta facendo per DOM.

Tu decidi.


Mi sono imbattuto nell'ostacolo di trattare con oggetti che usano chiusure per nascondere lo stato. Come si può clonare un oggetto e il suo intero stato ma assicurarsi che il clone non possa alterare da solo lo stato originale. Un punto importante per result = new item.constructor();essere cattivi è che, data la funzione di costruzione e l'oggetto item, dovresti essere in grado di RE qualsiasi parametro passato al costruttore.
Raynos

7
@ Raynos: se gli oggetti usano le chiusure per nascondere lo stato, non puoi clonarli. Da qui il termine "chiusura". Come dice nemisj alla fine, il modo migliore è implementare un metodo API per la clonazione (o serializzazione / deserializzazione) se questa è un'opzione.
Michiel Kalkman

@ MichielKalkman Ho avuto la sensazione che fosse così. Anche se qualcuno potrebbe aver trovato una soluzione davvero intelligente a questo.
Raynos

Qual è l'effetto di false && item.constructor? Non è ifinutile?
Gabriel Petrovay

2
@GabrielPetrovay Questo ifè "inutile" dal punto di vista funzionale, perché non verrà mai eseguito, ma ha lo scopo accademico di mostrare un'implementazione ipotetica che si potrebbe provare a utilizzare, che l'autore non consiglia per il motivo spiegato in seguito. Quindi, sì, attiverà la elseclausola ogni volta che la condizione viene valutata, eppure c'è una ragione per cui il codice è lì.
Gui Imamura

155

Modo molto semplice, forse troppo semplice:

var cloned = JSON.parse(JSON.stringify(objectToClone));

12
Ottimo a meno che il valore di un oggetto non sia una funzione, a quel punto dovrai usare qualcosa come la risposta accettata. Oppure usa una funzione di aiuto come cloneDeepin Lodash.
matthoiland

31
Se il valore di un oggetto è una funzione, l'oggetto non è JSON.
Jos de Jong

5
Quale caso d'uso può giustificare la clonazione di una funzione anziché il semplice utilizzo?
G. Ghez

3
Se ricordo bene questo converte anche le date in stringhe.
Peter,

3
@ G.Ghez se cloni un oggetto che contiene una funzione quella funzione andrà persa ..
Peter

38

La JSON.parse(JSON.stringify())combinazione per copiare in profondità gli oggetti Javascript è un hack inefficace, poiché era pensata per i dati JSON. Non supporta i valori di undefinedo function () {}e semplicemente li ignorerà (o nullli ignorerà ) durante la "stringa" (marshalling) dell'oggetto Javascript in JSON.

Una soluzione migliore è utilizzare una funzione di copia profonda. La funzione sotto copia in profondità gli oggetti e non richiede una libreria di terze parti (jQuery, LoDash, ecc.).

function copy(aObject) {
  if (!aObject) {
    return aObject;
  }

  let v;
  let bObject = Array.isArray(aObject) ? [] : {};
  for (const k in aObject) {
    v = aObject[k];
    bObject[k] = (typeof v === "object") ? copy(v) : v;
  }

  return bObject;
}

7
Tranne quando unObject (o un altro oggetto che contiene) contiene un riferimento a se stesso ... stackoverflow ™!
e2-e4

@ ringø - Potete fornire alcuni casi di test "autoreferenziali"?
tfmontague

4
var o = { a:1, b:2 } ; o["oo"] = { c:3, m:o };
e2-e4

4
Mi piace questa soluzione. L'unica soluzione per me era trattare con valori nulli: bObject[k] = (v === null) ? null : (typeof v === "object") ? copy(v) : v;
David Kirkland

2
Questa funzione è semplice, comprensibile e catturerà quasi tutti i casi. Nel mondo JavaScript questo è il più perfetto possibile.
icc97

21

Ecco una funzione ES6 che funzionerà anche per oggetti con riferimenti ciclici:

function deepClone(obj, hash = new WeakMap()) {
    if (Object(obj) !== obj) return obj; // primitives
    if (hash.has(obj)) return hash.get(obj); // cyclic reference
    const result = obj instanceof Set ? new Set(obj) // See note about this!
                 : obj instanceof Map ? new Map(Array.from(obj, ([key, val]) => 
                                        [key, deepClone(val, hash)])) 
                 : obj instanceof Date ? new Date(obj)
                 : obj instanceof RegExp ? new RegExp(obj.source, obj.flags)
                 // ... add here any specific treatment for other classes ...
                 // and finally a catch-all:
                 : obj.constructor ? new obj.constructor() 
                 : Object.create(null);
    hash.set(obj, result);
    return Object.assign(result, ...Object.keys(obj).map(
        key => ({ [key]: deepClone(obj[key], hash) }) ));
}

// Sample data
var p = {
  data: 1,
  children: [{
    data: 2,
    parent: null
  }]
};
p.children[0].parent = p;

var q = deepClone(p);

console.log(q.children[0].parent.data); // 1

Una nota su set e mappe

Come trattare le chiavi di Sets e Maps è discutibile: quelle chiavi sono spesso primitive (nel qual caso non c'è dibattito), ma possono anche essere oggetti. In quel caso la domanda diventa: quelle chiavi dovrebbero essere clonate?

Si potrebbe sostenere che ciò dovrebbe essere fatto, in modo che se quegli oggetti sono mutati nella copia, gli oggetti nell'originale non sono interessati, e viceversa.

D'altra parte si vorrebbe che se si imposta / mappa hasuna chiave, questo dovrebbe essere vero sia nell'originale che nella copia, almeno prima che venga apportata qualsiasi modifica a uno di essi. Sarebbe strano se la copia fosse un Set / Map che ha chiavi che non si sono mai verificate prima (come sono state create durante il processo di clonazione): sicuramente questo non è molto utile per qualsiasi codice che ha bisogno di sapere se un dato oggetto è un digitare quel Set / Map oppure no.

Come notate, sono più della seconda opinione: le chiavi di Sets e Maps sono valori (forse riferimenti ) che dovrebbero rimanere gli stessi.

Tali scelte spesso emergeranno anche con altri oggetti (forse personalizzati). Non esiste una soluzione generale, in quanto dipende da come si prevede che l'oggetto clonato si comporti nel tuo caso specifico.


non gestisce la data e l'
espressione regolare

1
@mkeremguc, grazie per il tuo commento. Ho aggiornato il codice per supportare date e regexp.
trincot

Bella soluzione comprensiva di mappa. Manca solo il supporto per la clonazione di set es6.
Robert Biggs,

Se non sbaglio, probabilmente puoi aggiungere il supporto per i set con:if (object instanceof Set) Array.from(object, val => result.add(deepClone(val, hash)));
Robert Biggs

@RobertBiggs, questa è una possibilità, ma secondo me se un Set ha una certa chiave, dovrebbe essere vero anche nella versione clonata di quel Set. Con il codice suggerito ciò non sarebbe vero se le chiavi fossero oggetti. Pertanto suggerirei di non clonare le chiavi - penso davvero che si comporterebbe quindi di più come previsto. Vedi l'aggiornamento della mia risposta a tale riguardo.
trincot

10

La libreria della libreria contrib Underscore.js ha una funzione chiamata snapshot che clona in profondità un oggetto

snippet dalla fonte:

snapshot: function(obj) {
  if(obj == null || typeof(obj) != 'object') {
    return obj;
  }

  var temp = new obj.constructor();

  for(var key in obj) {
    if (obj.hasOwnProperty(key)) {
      temp[key] = _.snapshot(obj[key]);
    }
  }

  return temp;
}

una volta che la libreria è collegata al tuo progetto, invoca la funzione semplicemente usando

_.snapshot(object);

4
buona soluzione, solo un punto da ricordare: il clone e l'originale condividono lo stesso prototipo. se questo è un problema è possibile aggiungere "temp .__ proto__ = .snapshot (obj .__ proto_ );" proprio sopra "return temp", e per supportare classi incorporate con proprietà contrassegnate come 'no enumerate' puoi iterare su getOwnPropertyNames () invece di "for (var key in obj)"
Ronen Ness

3

Questo è il metodo di clonazione profonda che uso, penso che sia fantastico, spero che tu dia suggerimenti

function deepClone (obj) {
    var _out = new obj.constructor;

    var getType = function (n) {
        return Object.prototype.toString.call(n).slice(8, -1);
    }

    for (var _key in obj) {
        if (obj.hasOwnProperty(_key)) {
            _out[_key] = getType(obj[_key]) === 'Object' || getType(obj[_key]) === 'Array' ? deepClone(obj[_key]) : obj[_key];
        }
    }
    return _out;
}

3

Come altri hanno notato su questa e altre domande simili, la clonazione di un "oggetto", in senso generale, è dubbia in JavaScript.

Tuttavia, esiste una classe di oggetti, che io chiamo oggetti "dati", cioè quelli costruiti semplicemente da { ... }letterali e / o semplici assegnazioni di proprietà o deserializzati da JSON per i quali è ragionevole voler clonare. Proprio oggi volevo gonfiare artificialmente i dati ricevuti da un server di 5 volte per testare cosa succede per un set di dati di grandi dimensioni, ma l'oggetto (un array) ei suoi figli dovevano essere oggetti distinti affinché le cose funzionassero correttamente. La clonazione mi ha permesso di farlo per moltiplicare il mio set di dati:

return dta.concat(clone(dta),clone(dta),clone(dta),clone(dta));

L'altro posto in cui spesso finisco per clonare oggetti dati è l'invio di dati all'host in cui desidero rimuovere i campi di stato dall'oggetto nel modello dati prima di inviarlo. Ad esempio, potrei voler rimuovere tutti i campi che iniziano con "_" dall'oggetto quando viene clonato.

Questo è il codice che ho finito per scrivere per farlo in modo generico, inclusi array di supporto e un selettore per scegliere quali membri clonare (che utilizza una stringa "percorso" per determinare il contesto):

function clone(obj,sel) {
    return (obj ? _clone("",obj,sel) : obj);
    }

function _clone(pth,src,sel) {
    var ret=(src instanceof Array ? [] : {});

    for(var key in src) {
        if(!src.hasOwnProperty(key)) { continue; }

        var val=src[key], sub;

        if(sel) {
            sub+=pth+"/"+key;
            if(!sel(sub,key,val)) { continue; }
            }

        if(val && typeof(val)=='object') {
            if     (val instanceof Boolean) { val=Boolean(val);        }
            else if(val instanceof Number ) { val=Number (val);        }
            else if(val instanceof String ) { val=String (val);        }
            else                            { val=_clone(sub,val,sel); }
            }
        ret[key]=val;
        }
    return ret;
    }

La soluzione di clonazione profonda più semplice e ragionevole, assumendo un oggetto radice non nullo e senza selezione di membri è:

function clone(src) {
    var ret=(src instanceof Array ? [] : {});
    for(var key in src) {
        if(!src.hasOwnProperty(key)) { continue; }
        var val=src[key];
        if(val && typeof(val)=='object') { val=clone(val);  }
        ret[key]=val;
        }
    return ret;
    }

2

Lo-Dash , ora un superset di Underscore.js , ha un paio di funzioni di clonazione profonda:

Da una risposta dell'autore stesso:

lodash underscore build viene fornito per garantire la compatibilità con l'ultima versione stabile di Underscore.


domanda diceva "non voglio usare le biblioteche"
Femi Oni

@FemiOni la domanda non ha nulla sulle librerie (anche nelle sue vecchie modifiche) ... Alcune altre risposte qui stanno usando anche una libreria o un'altra.
CPHPython

La risposta di @FemiOni è stata negata ieri. Mi chiedo ... Comunque, questo è un luogo di apprendimento e nel caso qualcuno stia effettivamente per implementare il clone profondo stesso, il codice sorgente di lodashbaseClone potrebbe fornire alcune idee.
CPHPython

@FemiOni l' oggetto JSON non è né una libreria né un framework ... Se hai intenzione di implementare la funzione, ti suggerisco di esaminare una delle librerie open source e di utilizzare le parti che ti servono (molte sono state testate per anni). Eviterà bug e considerazioni mancate a lungo termine.
CPHPython

2

La funzione seguente è il modo più efficiente per clonare in profondità gli oggetti JavaScript.

function deepCopy(obj){
    if (!obj || typeof obj !== "object") return obj;

    var retObj = {};

    for (var attr in obj){
        var type = obj[attr];

        switch(true){
            case (type instanceof Date):
                var _d = new Date();
                _d.setDate(type.getDate())
                retObj[attr]= _d;
                break;

            case (type instanceof Function):
                retObj[attr]= obj[attr];
                break;

            case (type instanceof Array):
                var _a =[];
                for (var e of type){
                    //_a.push(e);
                    _a.push(deepCopy(e));
                }
                retObj[attr]= _a;
                break;

            case (type instanceof Object):
                var _o ={};
                for (var e in type){
                    //_o[e] = type[e];
                    _o[e] = deepCopy(type[e]);
                }
                retObj[attr]= _o;
                break;

            default:
                retObj[attr]= obj[attr];
        }
    }
    return retObj;
}

var obj = {
    string: 'test',
    array: ['1'],
    date: new Date(),
    object:{c: 2, d:{e: 3}},
    function: function(){
        return this.date;
    }
};

var copyObj = deepCopy(obj);

console.log('object comparison', copyObj === obj); //false
console.log('string check', copyObj.string === obj.string); //true
console.log('array check', copyObj.array === obj.array); //false
console.log('date check', copyObj2.date === obj.date); //false
console.log('object check', copyObj.object === obj.object); //false
console.log('function check', copyObj.function() === obj.function()); //true

2
Hai degli argomenti a sostegno della tua richiesta?
Fabian von Ellerts

Ho fornito un esempio sotto la funzione. Hai qualche dubbio?
Kooldandy

Questi esempi mostrano che la funzione sta funzionando, il che è interessante, ma perché è "il modo più efficiente"?
Fabian von Ellerts

Perché sta copiando ricorsivamente gli attributi dell'oggetto in un singolo ciclo. Anche Data, Funzione, Oggetto, Matrice, Numero, Stringa vengono gestiti correttamente. Hai qualche altro modo per aggirare?
Kooldandy

2

Non dovrebbe più esserci bisogno nel mondo reale di tale funzione. Questo è mero interesse accademico.

Essendo puramente un esercizio, questo è un modo più funzionale per farlo. È un'estensione della risposta di @ tfmontague poiché avevo suggerito di aggiungere un blocco di guardia lì. Ma visto che mi sento obbligato a utilizzare ES6 e funzionalizzare tutte le cose, ecco la mia versione ottimizzata. Complica la logica in quanto è necessario mappare sull'array e ridurre l'oggetto, ma evita qualsiasi mutazione.

const cloner = (x) => {
    const recurseObj = x => (typeof x === 'object') ? cloner(x) : x
    const cloneObj = (y, k) => {
        y[k] = recurseObj(x[k])
        return y
    }
    // Guard blocks
    // Add extra for Date / RegExp if you want
    if (!x) {
        return x
    }
    if (Array.isArray(x)) {
        return x.map(recurseObj)
    }
    return Object.keys(x).reduce(cloneObj, {})
}
const tests = [
    null,
    [],
    {},
    [1,2,3],
    [1,2,3, null],
    [1,2,3, null, {}],
    [new Date('2001-01-01')], // FAIL doesn't work with Date
    {x:'', y: {yx: 'zz', yy: null}, z: [1,2,3,null]},
    {
        obj : new function() {
            this.name = "Object test";
        }
    } // FAIL doesn't handle functions
]
tests.map((x,i) => console.log(i, cloner(x)))


1

Ho notato che Map dovrebbe richiedere un trattamento speciale, quindi con tutti i suggerimenti in questo thread, il codice sarà:

function deepClone( obj ) {
    if( !obj || true == obj ) //this also handles boolean as true and false
        return obj;
    var objType = typeof( obj );
    if( "number" == objType || "string" == objType ) // add your immutables here
        return obj;
    var result = Array.isArray( obj ) ? [] : !obj.constructor ? {} : new obj.constructor();
    if( obj instanceof Map )
        for( var key of obj.keys() )
            result.set( key, deepClone( obj.get( key ) ) );
    for( var key in obj )
        if( obj.hasOwnProperty( key ) )
            result[key] = deepClone( obj[ key ] );
    return result;
}

1

la mia aggiunta a tutte le risposte

deepCopy = arr => {
  if (typeof arr !== 'object') return arr
  if(arr.pop) return [...arr].map(deepCopy)
  const copy = {}
  for (let prop in arr)
    copy[prop] = deepCopy(arr[prop])
  return copy
}

0

Funziona per array, oggetti e primitive. Algoritmo doppiamente ricorsivo che passa tra due metodi di attraversamento:

const deepClone = (objOrArray) => {

  const copyArray = (arr) => {
    let arrayResult = [];
    arr.forEach(el => {
        arrayResult.push(cloneObjOrArray(el));
    });
    return arrayResult;
  }

  const copyObj = (obj) => {
    let objResult = {};
    for (key in obj) {
      if (obj.hasOwnProperty(key)) {
        objResult[key] = cloneObjOrArray(obj[key]);
      }
    }
    return objResult;
  }

  const cloneObjOrArray = (el) => {
    if (Array.isArray(el)) {
      return copyArray(el);
    } else if (typeof el === 'object') {
      return copyObj(el);
    } else {
      return el;
    }
  }

  return cloneObjOrArray(objOrArray);
}

0

Possiamo utilizzare la ricorsione per creare deepCopy. Può creare copie di array, oggetti, array di oggetti, oggetti con funzioni. se lo desideri, puoi aggiungere funzioni per altri tipi di struttura dati come mappa ecc.

function deepClone(obj) {
         var retObj;
        _assignProps = function(obj, keyIndex, retObj) {
               var subType = Object.prototype.toString.call(obj[keyIndex]);
               if(subType === "[object Object]" || subType === "[object Array]") {
                    retObj[keyIndex] = deepClone(obj[keyIndex]);
               }
               else {
                     retObj[keyIndex] = obj[keyIndex];
               }
        };

        if(Object.prototype.toString.call(obj) === "[object Object]") {
           retObj = {};
           for(key in obj) {
               this._assignProps(obj, key, retObj);
           }
        }
        else if(Object.prototype.toString.call(obj) == "[object Array]") {
           retObj = [];
           for(var i = 0; i< obj.length; i++) {
              this._assignProps(obj, i, retObj);
            }
        };

        return retObj;
    };

0

Usa immutableJS

import { fromJS } from 'immutable';

// An object we want to clone
let objA = { 
   a: { deep: 'value1', moreDeep: {key: 'value2'} } 
};

let immB = fromJS(objA); // Create immutable Map
let objB = immB.toJS(); // Convert to plain JS object

console.log(objA); // Object { a: { deep: 'value1', moreDeep: {key: 'value2'} } }
console.log(objB); // Object { a: { deep: 'value1', moreDeep: {key: 'value2'} } }

// objA and objB are equalent, but now they and their inner objects are undependent
console.log(objA === objB); // false
console.log(objA.a === objB.a); // false
console.log(objA.moreDeep === objB.moreDeep); // false

O lodash / merge

import merge from 'lodash/merge'

var objA = {
    a: [{ 'b': 2 }, { 'd': 4 }]
};
// New deeply cloned object:
merge({}, objA ); 

// We can also create new object from several objects by deep merge:
var objB = {
    a: [{ 'c': 3 }, { 'e': 5 }]
};
merge({}, objA , objB ); // Object { a: [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }

0

Questo, usando il riferimento circolare, funziona per me

 //a test-object with circular reference :
 var n1 = {   id:0,   text:"aaaaa",   parent:undefined} 
 var n2 = {  id:1,   text:"zzzzz",   parent:undefined } 
 var o = { arr:[n1,n2],   parent:undefined } 
 n1.parent = n2.parent = o;
 var obj = {   a:1,   b:2,   o:o }
 o.parent = obj;

 function deepClone(o,output){ 

     if(!output) output = {};  
     if(o.______clone) return o.______clone;
     o.______clone = output.______clone = output;

   for(var z in o){

     var obj = o[z];
     if(typeof(obj) == "object") output[z] = deepClone(obj)
     else output[z] = obj; 
    }

   return output;
}

console.log(deepClone(obj));

0

var newDate = new Date (this.oldDate); Stavo passando oldDate alla funzione e generando newDate da this.oldDate, ma stava cambiando anche this.oldDate. Quindi ho usato quella soluzione e ha funzionato.


0

Questa soluzione eviterà problemi di ricorsione quando si utilizza [... target] o {... target}

function shallowClone(target) {
  if (typeof a == 'array') return [...target]
  if (typeof a == 'object') return {...target}
  return target
}

/* set skipRecursion to avoid throwing an exception on recursive references */
/* no need to specify refs, or path -- they are used interally */
function deepClone(target, skipRecursion, refs, path) {
  if (!refs) refs = []
  if (!path) path = ''
  if (refs.indexOf(target) > -1) {
    if (skipRecursion) return null
    throw('Recursive reference at ' + path)
  }
  refs.push(target)
  let clone = shallowCopy(target)
  for (i in target) target[i] = deepClone(target, refs, path + '.' + i)
  return clone
}
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.