Comment cloner en profondeur en javascript


105

Comment cloner en profondeur un objet Javascript?

Je sais qu'il existe diverses fonctions basées sur des frameworks comme JSON.parse(JSON.stringify(o))et $.extend(true, {}, o)mais je ne veux pas utiliser un framework comme celui-là.

Quelle est la manière la plus élégante ou la plus efficace de créer un clone profond.

Nous nous soucions des cas extrêmes tels que les tableaux de clonage. Ne pas briser les chaînes de prototypes, gérer l'auto-référence.

Nous ne nous soucions pas de prendre en charge la copie d'objets DOM et autres, car il .cloneNodeexiste pour cette raison.

Comme je veux principalement utiliser des clones profonds en node.jsutilisant les fonctionnalités ES5 du moteur V8, c'est acceptable.

[Éditer]

Avant que quiconque ne suggère, permettez-moi de mentionner qu'il existe une différence distincte entre la création d'une copie en héritant prototypiquement de l'objet et le clonage . Le premier gâche la chaîne du prototype.

[Modifier davantage]

Après avoir lu votre réponse, je suis venu à la découverte ennuyeuse que le clonage d'objets entiers est un jeu très dangereux et difficile. Prenons par exemple l'objet suivant basé sur la fermeture

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

Existe-t-il un moyen d'écrire une fonction de clonage qui clone l'objet, a le même état au moment du clonage mais ne peut pas modifier l'état de osans écrire un analyseur JS dans JS.

Il ne devrait plus y avoir de besoin réel d'une telle fonction. C'est un simple intérêt académique.


2
Avant qu'il ne soit marqué en double, j'ai regardé stackoverflow.com/questions/122102/ ... et je n'ai trouvé aucune réponse traitant de tous les cas marginaux.
Raynos

L'exigence dans la section "édition ultérieure" est impossible à réaliser sans "l'aide" de l'objet lui-même, puisque ces variables privées sont vraiment privées , et par conséquent non accessibles par une clonefonction générique . L'objet en question doit exposer sa propre cloneméthode sur mesure.
trincot le

J'ai lu un tas de choses à ce sujet ce soir, et parmi les ressources que j'ai trouvées, il y avait cet article de blog laid qui comprend quelques hacks pour accéder à l'algorithme de clone structuré dans le navigateur: dassur.ma/things/deep-copy
Cat

Réponses:


67

Cela dépend vraiment de ce que vous souhaitez cloner. S'agit-il d'un objet vraiment JSON ou de n'importe quel objet en JavaScript? Si vous souhaitez faire un clonage, cela pourrait vous causer des problèmes. Quel problème? Je vais l'expliquer ci-dessous, mais d'abord, un exemple de code qui clone les littéraux d'objet, les primitives, les tableaux et les nœuds 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";
            }   
        }
    ]
})

Et maintenant, parlons des problèmes que vous pourriez rencontrer lorsque vous commencez à cloner des objets RÉELS. Je parle maintenant d'objets que vous créez en faisant quelque chose comme

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

Bien sûr, vous pouvez les cloner, ce n'est pas un problème, chaque objet expose la propriété du constructeur et vous pouvez l'utiliser pour cloner des objets, mais cela ne fonctionnera pas toujours. Vous pouvez également faire simple for insur ces objets, mais cela va dans le même sens - problème. J'ai également inclus la fonctionnalité de clonage dans le code, mais elle est exclue par if( false )déclaration.

Alors, pourquoi le clonage peut être une douleur? Eh bien, tout d'abord, chaque objet / instance peut avoir un état. Vous ne pouvez jamais être sûr que vos objets n'ont pas par exemple de variables privées, et si tel est le cas, en clonant un objet, vous cassez simplement l'état.

Imaginez qu'il n'y a pas d'état, c'est bien. Ensuite, nous avons encore un autre problème. Le clonage via la méthode "constructeur" nous donnera un autre obstacle. C'est une dépendance d'arguments. Vous ne pouvez jamais être sûr que quelqu'un qui a créé cet objet n'a pas fait, une sorte de

new User({
   bike : someBikeInstance
});

Si tel est le cas, vous n'avez pas de chance, someBikeInstance a probablement été créé dans un certain contexte et ce contexte est inconnu pour la méthode de clonage.

Alors que faire? Vous pouvez toujours faire une for insolution et traiter ces objets comme des littéraux d'objet normaux, mais peut-être que c'est une idée de ne pas cloner du tout de tels objets et de simplement passer la référence de cet objet?

Une autre solution est - vous pouvez définir une convention selon laquelle tous les objets qui doivent être clonés devraient implémenter cette partie par eux-mêmes et fournir la méthode API appropriée (comme cloneObject). Quelque chose de ce que cloneNodefait pour DOM.

Tu décides.


Je suis venu après l'obstacle de traiter des objets qui utilisent des fermetures pour cacher l'état moi-même. Comment peut-on cloner un objet et son état entier tout en s'assurant que le clone ne peut pas modifier l'état d'origine par lui-même. Un point culminant à result = new item.constructor();être mauvais est que, étant donné la fonction du constructeur et l'objet item, vous devriez être capable de RE tous les paramètres passés dans le constructeur.
Raynos

7
@Raynos: si les objets utilisent des fermetures pour masquer l'état, vous ne pouvez pas les cloner. D'où le terme «fermeture». Comme le dit nemisj à la fin, le meilleur moyen est d'implémenter une méthode API pour le clonage (ou la sérialisation / désérialisation) si c'est une option.
Michiel Kalkman

@MichielKalkman J'avais le sentiment que c'était le cas. Bien que quelqu'un ait pu avoir une solution vraiment intelligente à cela.
Raynos

Quel est l'effet de false && item.constructor? N'est-ce pas ifinutile?
Gabriel Petrovay

2
@GabrielPetrovay C'est if«inutile» du point de vue fonctionnel, car il ne fonctionnera jamais, mais il a pour but académique de montrer une implémentation hypothétique que l'on pourrait essayer d'utiliser, que l'auteur ne conseille pas à cause de la raison expliquée plus tard. Donc, oui, cela déclenchera la elseclause chaque fois que la condition est évaluée, et pourtant il y a une raison pour que le code soit là.
Gui Imamura

155

Manière très simple, peut-être trop simple:

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

12
Excellent sauf si une valeur d'objet est une fonction, à quel point vous devrez utiliser quelque chose comme la réponse acceptée. Ou utilisez une fonction d'aide comme cloneDeepdans Lodash.
matthoiland

31
Si une valeur d'objet est une fonction, l'objet n'est pas JSON.
Jos de Jong

5
Quel cas d'utilisation peut justifier de cloner une fonction plutôt que de simplement l'utiliser?
G.Ghez

3
Si je me souviens bien, cela convertit également les dates en chaînes.
Peter

3
@ G.Ghez si vous clonez un objet qui contient une fonction, cette fonction sera perdue ..
Peter

38

La JSON.parse(JSON.stringify())combinaison pour copier en profondeur des objets Javascript est un hack inefficace, car elle était destinée aux données JSON. Il ne prend pas en charge les valeurs de undefinedou function () {}, et les ignorera simplement (ou nullles) lors de la "stringification" (marshalling) de l'objet Javascript en JSON.

Une meilleure solution consiste à utiliser une fonction de copie profonde. La fonction ci-dessous copie en profondeur les objets et ne nécessite pas de bibliothèque tierce (jQuery, LoDash, etc.).

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
Sauf lorsqu'un objet (ou un autre objet qu'il contient) contient une auto-référence à lui-même ... stackoverflow ™!
e2-e4

@ ringø - Pouvez-vous fournir des cas de test "d'auto-référence"?
tfmontague

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

4
J'aime cette solution. Le seul correctif pour moi concernait les valeurs nulles: bObject[k] = (v === null) ? null : (typeof v === "object") ? copy(v) : v;
David Kirkland

2
Cette fonction est simple, compréhensible et capturera presque tous les cas. Dans le monde JavaScript, c'est à peu près aussi parfait que possible.
icc97

21

Voici une fonction ES6 qui fonctionnera également pour les objets avec des références cycliques:

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

Une note sur les ensembles et les cartes

Comment gérer les clés des Sets et des Maps est discutable: ces clés sont souvent primitives (auquel cas il n'y a pas de débat), mais elles peuvent aussi être des objets. Dans ce cas, la question devient: ces clés doivent-elles être clonées?

On pourrait soutenir que cela devrait être fait, de sorte que si ces objets sont mutés dans la copie, les objets de l'original ne sont pas affectés, et vice versa.

D'un autre côté, on voudrait que si un Set / Map a hasune clé, cela devrait être vrai à la fois dans l'original et dans la copie - au moins avant qu'une modification ne soit apportée à l'un ou l'autre. Il serait étrange que la copie soit un Set / Map qui a des clés qui ne se sont jamais produites auparavant (comme elles ont été créées pendant le processus de clonage): ce n'est sûrement pas très utile pour tout code qui a besoin de savoir si un objet donné est un saisissez ou non cet ensemble / carte.

Comme vous le remarquez, je suis plus du second avis: les clés des Sets et des Maps sont des valeurs (peut-être des références ) qui devraient rester les mêmes.

De tels choix apparaîtront souvent avec d'autres objets (peut-être personnalisés). Il n'y a pas de solution générale, car tout dépend du comportement attendu de l'objet cloné dans votre cas particulier.


ne gère pas la date et l'expression
régulière

1
@mkeremguc, merci pour votre commentaire. J'ai mis à jour le code pour prendre en charge la date et l'expression régulière.
trincot

Belle solution, y compris la carte. Manque juste de support pour le clonage des ensembles es6.
Robert Biggs

Si je ne me trompe pas, vous pouvez probablement ajouter un support pour les sets avec:if (object instanceof Set) Array.from(object, val => result.add(deepClone(val, hash)));
Robert Biggs

@RobertBiggs, c'est une possibilité, mais à mon avis, si un ensemble a une certaine clé, cela devrait également être vrai dans la version clonée de cet ensemble. Avec votre code suggéré, cela ne serait pas vrai si les clés sont des objets. Par conséquent, je suggérerais de ne pas cloner les clés - je pense vraiment que cela se comporterait alors plus comme prévu. Voir mise à jour de ma réponse à cet égard.
trincot

10

La bibliothèque de la bibliothèque contrib Underscore.js a une fonction appelée snapshot qui clone en profondeur un objet

extrait de la source:

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

une fois la bibliothèque liée à votre projet, appelez la fonction en utilisant simplement

_.snapshot(object);

4
bonne solution, juste un point à retenir: le clone et l'original partagent le même prototype. si c'est un problème, il est possible d'ajouter "temp .__ proto__ = .snapshot (obj .__ proto_ );" juste au-dessus de "return temp", et pour prendre en charge les classes intégrées avec des propriétés marquées comme "no enumerate", vous pouvez itérer sur getOwnPropertyNames () au lieu de "for (var key in obj)"
Ronen Ness

3

C'est la méthode de clonage profond que j'utilise, je pense que c'est génial, j'espère que vous faites des suggestions

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

Comme d'autres l'ont noté sur cette question et d'autres similaires, le clonage d'un "objet", au sens général, est douteux en JavaScript.

Cependant, il existe une classe d'objets, que j'appelle les objets «data», c'est-à-dire ceux construits simplement à partir de { ... }littéraux et / ou d'attributions de propriétés simples ou désérialisés de JSON pour lesquels il est raisonnable de vouloir cloner. Aujourd'hui encore, je voulais gonfler artificiellement les données reçues d'un serveur de 5x pour tester ce qui se passe pour un grand ensemble de données, mais l'objet (un tableau) et ses enfants devaient être des objets distincts pour que les choses fonctionnent correctement. Le clonage m'a permis de faire cela pour multiplier mon ensemble de données:

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

L'autre endroit où je finis souvent par cloner des objets de données est de soumettre des données à l'hôte où je souhaite supprimer les champs d'état de l'objet dans le modèle de données avant de les envoyer. Par exemple, je pourrais vouloir supprimer tous les champs commençant par "_" de l'objet lorsqu'il est cloné.

C'est le code que j'ai fini par écrire pour le faire de manière générique, y compris les tableaux de support et un sélecteur pour choisir les membres à cloner (qui utilise une chaîne de "chemin" pour déterminer le contexte):

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 solution de clone profond raisonnable la plus simple, en supposant un objet racine non nul et sans sélection de membre est:

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 , maintenant un sur-ensemble de Underscore.js , a quelques fonctions de clonage profond:

D'après une réponse de l'auteur lui-même:

lodash underscore build est fourni pour assurer la compatibilité avec la dernière version stable de Underscore.


question dit "je ne veux pas utiliser les bibliothèques"
Femi Oni

@FemiOni la question n'a rien sur les bibliothèques (même dans ses anciennes modifications) ... Quelques autres réponses ici utilisent également une bibliothèque ou une autre.
CPHPython

La réponse @FemiOni a été rejetée hier. Je me demande ... Quoi qu'il en soit, c'est un lieu d'apprentissage et juste au cas où quelqu'un implémenterait le clone profond lui-même, le code source de lodashbaseClone peut fournir quelques idées.
CPHPython

@FemiOni l' objet JSON n'est ni une bibliothèque ni un framework ... Si vous allez implémenter la fonction, je vous suggère de regarder dans l'une des bibliothèques open source et d'utiliser les parties dont vous avez besoin (beaucoup sont testées depuis des années). Cela évitera les bugs et les considérations manquées sur le long terme.
CPHPython

2

La fonction ci-dessous est le moyen le plus efficace de cloner en profondeur des objets 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
Avez-vous des arguments à l'appui de votre affirmation?
Fabian von Ellerts

J'ai donné un exemple ci-dessous la fonction. Avez-vous des doutes?
Kooldandy

Ces exemples montrent que la fonction fonctionne, ce qui est cool mais pourquoi est-ce "la manière la plus efficace"?
Fabian von Ellerts

Parce qu'il copie récursivement les attributs de l'objet en une seule boucle. De plus, la date, la fonction, l'objet, le tableau, le nombre, la chaîne sont tous gérés correctement. Avez-vous une autre solution?
Kooldandy

2

Il ne devrait plus y avoir de besoin réel d'une telle fonction. C'est un simple intérêt académique.

En tant que pur exercice, il s'agit d'une manière plus fonctionnelle de le faire. C'est une extension de la réponse de @ tfmontague car j'avais suggéré d' y ajouter un bloc de garde. Mais vu que je me sens obligé d'ES6 et de fonctionnaliser tout, voici ma version soutenue. Cela complique la logique car vous devez mapper sur le tableau et réduire sur l'objet, mais cela évite toute mutation.

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

J'ai remarqué que Map devrait nécessiter un traitement spécial, donc avec toutes les suggestions de ce fil, le code sera:

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

mon ajout à toutes les réponses

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

Cela fonctionne pour les tableaux, les objets et les primitives. Algorithme doublement récursif qui bascule entre deux méthodes de parcours:

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

Nous pouvons utiliser la récursivité pour créer deepCopy. Il peut créer une copie de tableau, objet, tableau d'objet, objet avec fonction. si vous le souhaitez, vous pouvez ajouter une fonction pour un autre type de structure de données comme une carte, etc.

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

Utilisez 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

Ou lodash / fusionner

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

Celui-ci, utilisant une référence circulaire, fonctionne pour moi

 //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 = nouvelle date (this.oldDate); Je passais oldDate pour fonctionner et générer newDate à partir de this.oldDate, mais cela changeait également this.oldDate.J'ai donc utilisé cette solution et cela a fonctionné.


0

Cette solution évitera les problèmes de récursivité lors de l'utilisation de [... target] ou {... 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
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.