Quel est le moyen le plus efficace de cloner en profondeur un objet en javascript?

Quel est le moyen le plus efficace de cloner un objet JavaScript? J'ai vu obj = eval(uneval(o)); mais qui est non standard et supporté uniquement par Firefox .

J'ai fait des choses comme obj = JSON.parse(JSON.stringify(o)); , mais doute de l'efficacité.

J'ai aussi vu les fonctions de copie récursives avec divers défauts.

Je suis surpris qu'il n'y ait pas de solution canonique.

4845
23 сент. réglé par jschrab le 23 sep . 2008-09-23 19:26 '08 à 19h26 2008-09-23 19:26
@ 69 réponses
  • 1
  • 2
  • 3

Remarque: Ceci est la réponse à une autre réponse, pas la réponse correcte à cette question. Si vous souhaitez rapidement cloner des objets, veuillez suivre les conseils de Corban dans votre réponse à cette question.


Je tiens à noter que la méthode .clone() dans jQuery ne clone que des éléments DOM. Pour cloner des objets JavaScript, vous devez:

 // Shallow copy var newObject = jQuery.extend({}, oldObject); // Deep copy var newObject = jQuery.extend(true, {}, oldObject); 

Vous trouverez plus d'informations dans la documentation de jQuery .

Je tiens également à souligner qu'une copie en profondeur est en réalité beaucoup plus intelligente que ce qui est montré ci-dessus - elle permet d'éviter de nombreux pièges (par exemple, pour développer profondément l'élément DOM). Il est souvent utilisé dans le noyau jQuery et dans les plugins avec beaucoup d’effet.

4203
23 сент. Réponse donnée par John Resig le 23 septembre 2008-09-23 21:09 2008 à 21h09 2008-09-23 21h09

Découvrez ce benchmark: http://jsben.ch/#/bWfk9

Lors de mes tests précédents, où la vitesse était le principal problème, j'ai découvert

<Précédent> <code> Code JSON.parse (Code JSON.stringify (OBJ))>

être le moyen le plus rapide de cloner en profondeur un objet (il dépasse jQuery.extend avec l’indicateur deep défini par 10-20%).

jQuery.extend est assez rapide lorsque l'indicateur de valeur profonde est défini sur false (clone superficiel). C'est une bonne option, car elle inclut une logique supplémentaire pour la vérification de type et ne copie pas les propriétés d'undefined, etc. Mais cela vous ralentira également un peu.

Si vous connaissez la structure des objets que vous essayez de cloner ou si vous pouvez éviter les tableaux imbriqués profonds, vous pouvez écrire une simple boucle for (var я in obj) pour cloner votre objet, en vérifiant hasOwnProperty et ce sera beaucoup plus rapide que jQuery.

border=0

Enfin, si vous essayez de cloner une structure d'objet connue dans une boucle dynamique, vous pouvez obtenir BEAUCOUP PLUS DE PERFORMANCES en insérant simplement la procédure de clonage et en créant manuellement l'objet.

Les mécanismes de suivi JavaScript sont for..in optimiser..les boucles et la vérification de hasOwnProperty vous ralentira également. Clonage manuel lorsque la vitesse est une nécessité absolue.

  var clonedObject = { knownProp: obj.knownProp,.. } Код> 

Faites attention à l’utilisation de la méthode JSON.parse(JSON.stringify(obj)) pour les objets Date - JSON.stringify(новая дата()) renvoie une représentation sous forme de chaîne de la date au format ISO dans laquelle JSON.parse() ne renvoie pas l’objet Date . Voir cette réponse pour plus de détails .

De plus, notez que dans Chrome 65, au moins, le clonage natif ne convient pas. Selon ce JSPerf , faire votre propre clonage en créant une nouvelle fonction est presque 800 fois plus lent qu'en utilisant JSON.stringify, qui passe incroyablement vite à travers le tableau.

2046
17 марта '11 в 22:19 2011-03-17 22:19 la réponse est donnée par Corban Brook le 17 mars 2011 à 22h19. 2011-03-17 22:19

En supposant que vous n’ayez que des variables et pas de fonctions dans votre objet, vous pouvez simplement utiliser:

 var newObject = JSON.parse(JSON.stringify(oldObject)); 
431
04 янв. La réponse est donnée par Sultan Shakir Jan 04 2011-01-04 11:05 '11 à 11:05 2011-01-04 11:05

Clonage structuré

Le standard HTML inclut un algorithme interne de clonage / sérialisation structuré pouvant créer des clones d'objets profonds. Il est toujours limité à certains types intégrés, mais en plus de plusieurs types pris en charge par JSON, il prend également en charge les dates, les régularisations, les cartes, les ensembles, les blobs, les listes de fichiers, les fichiers ImageDatas, les tableaux fragmentés, les tableaux typés, et peut-être plus à l'avenir. ., Il stocke également les références dans les données clonées, ce qui vous permet de conserver des structures cycliques et récursives pouvant générer des erreurs pour JSON.

Support dans Node.js: expérimental

Le module v8 présent dans Node.js (à partir du nœud 11) fournit directement une API de sérialisation structurée , mais cette fonctionnalité est toujours marquée comme «expérimentale» et peut être modifiée ou supprimée dans les versions ultérieures. Si vous utilisez une version compatible, cloner un objet est aussi simple que:

06 июня '12 в 17:59 2012-06-06 17:59 la réponse est donnée à Jeremy Banks le 06 juin 12 à 17:59 2012-06-06 17:59

Si ce n’est pas intégré, vous pouvez essayer:

 function clone(obj) { if (obj === null || typeof (obj) !== 'object' || 'isActiveClone' in obj) return obj; if (obj instanceof Date) var temp = new obj.constructor(); //or new Date(obj); else var temp = obj.constructor(); for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { obj['isActiveClone'] = null; temp[key] = clone(obj[key]); delete obj['isActiveClone']; } } return temp; } 
294
23 сент. Réponse donnée par ConroyP le 23 septembre 2008-09-23 19:38 2008 à 19h38 2008-09-23 19:38

Moyen efficace de cloner (pas de cloner en profondeur) un objet dans une ligne de code

La méthode Object.assign fait partie de la norme ECMAScript 2015 (ES6) et fait exactement ce dont vous avez besoin.

 var clone = Object.assign({}, obj); 

La méthode Object.assign () est utilisée pour copier les valeurs de toutes les propriétés appropriées énumérées d'un ou de plusieurs objets source vers l'objet cible.

Lire plus ...

polyfill pour supporter les anciens navigateurs:

 if (!Object.assign) { Object.defineProperty(Object, 'assign', { enumerable: false, configurable: true, writable: true, value: function(target) { 'use strict'; if (target === undefined || target === null) { throw new TypeError('Cannot convert first argument to object'); } var to = Object(target); for (var i = 1; i < arguments.length; i++) { var nextSource = arguments[i]; if (nextSource === undefined || nextSource === null) { continue; } nextSource = Object(nextSource); var keysArray = Object.keys(nextSource); for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) { var nextKey = keysArray[nextIndex]; var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey); if (desc !== undefined  desc.enumerable) { to[nextKey] = nextSource[nextKey]; } } } return to; } }); } 
148
15 дек. La réponse est donnée par Eugene Tiurin le 15 décembre. 2015-12-15 10:26 '15 à 10h26 2015-12-15 10h26

code:

 // extends 'from' object with members from 'to'. If 'to' is null, a deep clone of 'from' is returned function extend(from, to) { if (from == null || typeof from != "object") return from; if (from.constructor != Object  from.constructor != Array) return from; if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function || from.constructor == String || from.constructor == Number || from.constructor == Boolean) return new from.constructor(from); to = to || new from.constructor(); for (var name in from) { to[name] = typeof to[name] == "undefined" ? extend(from[name], null) : to[name]; } return to; } 

Test:

 var obj = { date: new Date(), func: function(q) { return 1 + q; }, num: 123, text: "asdasd", array: [1, "asd"], regex: new RegExp(/aaa/i), subobj: { num: 234, text: "asdsaD" } } var clone = extend(obj); 
94
25 июня '09 в 10:53 2009-06-25 10:53 la réponse est donnée par Kamarey le 25 juin 09 à 10:53. 2009-06-25 10:53

C'est ce que j'utilise:

 function cloneObject(obj) { var clone = {}; for(var i in obj) { if(typeof(obj[i])=="object"  obj[i] != null) clone[i] = cloneObject(obj[i]); else clone[i] = obj[i]; } return clone; } 
86
12 дек. La réponse est donnée à Alan le 12 décembre. 2009-12-12 01:47 '09 à 1:47 2009-12-12 01:47

Performance de copie profonde: du meilleur au pire

  • Réattribution "=" (tableaux de chaînes, tableaux numériques - uniquement)
  • Slice (tableaux de chaînes, tableaux de nombres uniquement)
  • Concaténation (uniquement tableaux de chaînes, tableaux numériques)
  • Fonction personnalisée: copie en boucle ou récursive
  • jQuery $ .extend
  • JSON.parse (uniquement des tableaux de chaînes, des tableaux de nombres, des tableaux d'objets)
  • Underscore.js _.clone (uniquement les tableaux de chaînes, tableaux numériques)
  • Lo-Dash _.cloneDeep

Copiez profondément un tableau de chaînes ou de nombres (un niveau - pas de pointeurs):

Lorsqu'un tableau contient des nombres et des chaînes, des fonctions telles que.slice (),. Concat (),. Splice (), l'opérateur d'affectation "=" et la fonction de clonage Underscore.js; faire une copie profonde des éléments du tableau.

Si la réaffectation présente les performances les plus élevées:

 var arr1 = ['a', 'b', 'c']; var arr2 = arr1; arr1 = ['a', 'b', 'c']; 

I.slice () a de meilleures performances que .concat (), http://jsperf.com/duplicate-array-slice-vs-concat/3

 var arr1 = ['a', 'b', 'c']; // Becomes arr1 = ['a', 'b', 'c'] var arr2a = arr1.slice(0); // Becomes arr2a = ['a', 'b', 'c'] - deep copy var arr2b = arr1.concat(); // Becomes arr2b = ['a', 'b', 'c'] - deep copy 

Copiez profondément un tableau d'objets (deux niveaux ou plus - pointeurs):

 var arr1 = [{object:'a'}, {object:'b'}]; 

Ecrivez une fonction personnalisée (a de meilleures performances que $ .extend () ou JSON.parse):

 function copy(o) { var out, v, key; out = Array.isArray(o) ? [] : {}; for (key in o) { v = o[key]; out[key] = (typeof v === "object"  v !== null) ? copy(v) : v; } return out; } copy(arr1); 

Utilisez des fonctions utilitaires tierces:

 $.extend(true, [], arr1); // Jquery Extend JSON.parse(arr1); _.cloneDeep(arr1); // Lo-dash 

Où jQuery $ .extend offre de meilleures performances:

71
18 сент. la réponse est donnée tfmontague le 18 sep 2014-09-18 23:10 '14 à 23:10 2014-09-18 23:10
 var clone = function() { var newObj = (this instanceof Array) ? [] : {}; for (var i in this) { if (this[i]  typeof this[i] == "object") { newObj[i] = this[i].clone(); } else { newObj[i] = this[i]; } } return newObj; }; Object.defineProperty( Object.prototype, "clone", {value: clone, enumerable: false}); 
60
26 дек. la réponse est donnée par Zibri le 26 décembre. 2009-12-26 17:59 '09 à 17:59 2009-12-26 17:59

Je sais qu'il s'agit d'un ancien post, mais je pensais que cela pourrait aider quelqu'un qui trébuche.

Tant que vous n'attribuez pas d'objet à quelque chose, il ne conserve pas de référence en mémoire. Ainsi, pour créer un objet que vous souhaitez partager avec d'autres objets, vous devez créer une fabrique comme celle-ci:

 var a = function(){ return { father:'zacharias' }; }, b = a(), c = a(); c.father = 'johndoe'; alert(b.father); 
53
24 сент. réponse donnée par Joe le 24 septembre 2011-09-24 22:28 '11 à 22:28 2011-09-24 22:28

Cloning objet a toujours été un sujet de préoccupation dans JS, mais c’était avant ES6, j’énumère différentes manières de copier un objet en JavaScript, imaginez que vous avez un objet ci-dessous et que vous souhaitez en avoir une copie complète:

 var obj = {a:1, b:2, c:3, d:4}; 

Il y a plusieurs façons de copier cet objet sans changer la source:

1) ES5 + utilisant une fonction simple pour copier:

 function deepCopyObj(obj) { if (null == obj || "object" != typeof obj) return obj; if (obj instanceof Date) { var copy = new Date(); copy.setTime(obj.getTime()); return copy; } if (obj instanceof Array) { var copy = []; for (var i = 0, len = obj.length; i < len; i++) { copy[i] = cloneSO(obj[i]); } return copy; } if (obj instanceof Object) { var copy = {}; for (var attr in obj) { if (obj.hasOwnProperty(attr)) copy[attr] = cloneSO(obj[attr]); } return copy; } throw new Error("Unable to copy obj this object."); } 

2) ES5 + utilisant JSON.parse et JSON.stringify.

 var deepCopyObj = JSON.parse(JSON.stringify(obj)); 

3) AngularJs:

 var deepCopyObj = angular.copy(obj); 

4) jQuery:

 var deepCopyObj = jQuery.extend(true, {}, obj); 

5) UnderscoreJs et Loadash:

 var deepCopyObj = _.cloneDeep(obj); //latest version UndescoreJs makes shallow copy 

J'espère que cette aide ...

52
03 апр. La réponse est donnée à Alireza le 03 avril. 2017-04-03 18:37 '17 à 18h37 2017-04-03 18:37

Theres une bibliothèque (appelée "clone") , ce qui le rend très bien. Il fournit le clonage / la copie récursive le plus complet d'objets arbitraires que je connaisse. Il prend également en charge les liens circulaires qui ne sont pas encore couverts par d'autres réponses.

Vous pouvez le trouver sur NPM . Il peut être utilisé à la fois pour le navigateur et pour Node.js.

Voici un exemple d'utilisation:

Installez-le avec

 npm install clone 

ou emballez-le avec Ender .

 ender build clone [...] 

Vous pouvez également télécharger le code source manuellement.

Ensuite, vous pouvez l'utiliser dans votre code source.

 var clone = require('clone'); var a = { foo: { bar: 'baz' } }; // inital value of a var b = clone(a); // clone a -> b a.foo.bar = 'foo'; // change a console.log(a); // { foo: { bar: 'foo' } } console.log(b); // { foo: { bar: 'baz' } } 

(Avertissement: je suis l'auteur de la bibliothèque.)

51
17 окт. la réponse est donnée pvorb 17 oct. 2012-10-17 21:36 '12 à 21:36 2012-10-17 21:36

Si vous l'utilisez, la bibliothèque Underscore.js a un clone .

 var newObject = _.clone(oldObject); 
48
15 дек. la réponse est donnée par itsadok 15 déc. 2011-12-15 18:56 '11 à 18:56 2011-12-15 18:56

Voici la version ci-dessus de ConroyP, qui fonctionne même si le concepteur a besoin de paramètres:

 //If Object.create isn't already defined, we just do the simple shim, //without the second argument, since that 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(); }; } function deepCopy(obj) { if(obj == null || typeof(obj) !== 'object'){ return obj; } //make sure the returned object has the same prototype as the original var ret = object_create(obj.constructor.prototype); for(var key in obj){ ret[key] = deepCopy(obj[key]); } return ret; } 

Cette fonctionnalité est également disponible dans ma bibliothèque simpleoo .

Modifier:

Voici une version plus fiable (grâce à Justin McCandles, elle supporte maintenant les références circulaires):

  function deepCopy(src,  _visited, _copiesVisited) { if(src === null || typeof(src) !== 'object'){ return src; } //Honor native/custom clone methods if(typeof src.clone == 'function'){ return src.clone(true); } //Special cases: //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); } // Initialize the visited objects arrays if needed. // This is used to detect cyclic references. if (_visited === undefined){ _visited = []; _copiesVisited = []; } // Check if this object has already been visited var i, len = _visited.length; for (i = 0; i < len; i++) { // If so, get the copy we already made if (src === _visited[i]) { return _copiesVisited[i]; } } //Array if (Object.prototype.toString.call(src) == '[object Array]') { //[].slice() by itself would soft clone var ret = src.slice(); //add it to the visited array _visited.push(src); _copiesVisited.push(ret); var i = ret.length; while (i--) { ret[i] = deepCopy(ret[i], _visited, _copiesVisited); } return ret; } //If we've reached here, we have a regular object //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 dest = object_create(proto); //add this object to the visited array _visited.push(src); _copiesVisited.push(dest); 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 dest[key] = deepCopy(src[key], _visited, _copiesVisited); } return dest; } //If Object.create isn't already defined, we just do the simple shim, //without the second argument, since that 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(); }; } 
36
11 нояб. réponse donnée par Matt Browne 11 nov. 2012-11-11 20:53 '12 à 20h53 2012-11-11 20:53

Ce qui suit crée deux instances du même objet. Je l'ai trouvé et l'utiliser maintenant. C'est simple et facile à utiliser.

 var objToCreate = JSON.parse(JSON.stringify(cloneThis)); 
31
21 авг. Réponse donnée par nathan rogers le 21 août 2015-08-21 18:51 '15 à 18h51 2015-08-21 18h51

Copier en profondeur des objets en javascript (je pense le meilleur et le plus simple)

1. Utilisation de JSON.parse (JSON.stringify (objet));

 var obj = { a: 1, b: { c: 2 } } var newObj = JSON.parse(JSON.stringify(obj)); obj.bc = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 2 } } 

2. Utilisation de la méthode créée

 function cloneObject(obj) { var clone = {}; for(var i in obj) { if(obj[i] != null  typeof(obj[i])=="object") clone[i] = cloneObject(obj[i]); else clone[i] = obj[i]; } return clone; } var obj = { a: 1, b: { c: 2 } } var newObj = cloneObject(obj); obj.bc = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 2 } } 

3. Utilisation du lien Lo-Dash _.cloneDeep lodash

 var obj = { a: 1, b: { c: 2 } } var newObj = _.cloneDeep(obj); obj.bc = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 2 } } 

4. Utilisation de Object.assign ()

 var obj = { a: 1, b: 2 } var newObj = _.clone(obj); obj.b = 20; console.log(obj); // { a: 1, b: 20 } console.log(newObj); // { a: 1, b: 2 } 

MAIS FAUX QUAND

 var obj = { a: 1, b: { c: 2 } } var newObj = Object.assign({}, obj); obj.bc = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG // Note: Properties on the prototype chain and non-enumerable properties cannot be copied. 

5. Utilisation du lien Underscore.js _.clone Underscore.js

 var obj = { a: 1, b: 2 } var newObj = _.clone(obj); obj.b = 20; console.log(obj); // { a: 1, b: 20 } console.log(newObj); // { a: 1, b: 2 } 

MAIS FAUX QUAND

 var obj = { a: 1, b: { c: 2 } } var newObj = _.cloneDeep(obj); obj.bc = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG // (Create a shallow-copied clone of the provided plain object. Any nested objects or arrays will be copied by reference, not duplicated.) 

Lien medium.com

JSBEN.CH Analyse comparative des performances Playground 1 ~ 3 http://jsben.ch/KVQLd 2019

08 авг. la réponse est donnée TinhNQ 08 août. 2018-08-08 11:17 '18 à 11h17 ; 2018-08-08 11h17

Lodash a une bonne méthode . cloneDeep (valeur) :

 var objects = [{ 'a': 1 }, { 'b': 2 }]; var deep = _.cloneDeep(objects); console.log(deep[0] === objects[0]); // => false 
23
22 июня '13 в 18:03 2013-06-22 18:03 la réponse est donnée par opensas le 22 juin '13 à 18:03 2013-06-22 18:03

Crockford suggère (et je préfère) d'utiliser cette fonctionnalité:

 function object(o) { function F() {} F.prototype = o; return new F(); } var newObject = object(oldObject); 

Il est court, fonctionne comme prévu et vous n’avez pas besoin d’une bibliothèque.


EDIT:

Ceci est un polyfill pour Object.create , vous pouvez donc également l'utiliser.

 var newObject = Object.create(oldObject); 

NOTE . Si vous en utilisez, vous pouvez avoir des problèmes avec l'itération, qui utilise hasOwnProperty . Parce que create crée un nouvel objet vide qui hérite de oldObject . Mais cela reste utile et pratique pour cloner des objets.

Par exemple, si oldObject.a = 5;

 newObject.a; // is 5 

un

 oldObject.hasOwnProperty(a); // is true newObject.hasOwnProperty(a); // is false 
23
06 окт. réponse donnée par Chris Broski 06 oct. 2010-10-06 18:08 '10 à 18:08 2010-10-06 18:08
 function clone(obj) { var clone = {}; clone.prototype = obj.prototype; for (property in obj) clone[property] = obj[property]; return clone; } 
22
23 сент. Réponse donnée par Mark Cidade le 23 septembre 2008-09-23 19:45 '08 à 19h45 2008-09-23 19:45

Copie simple ligne d'une copie superficielle ( ECMAScript 5ème édition ):

 var origin = { foo : {} }; var copy = Object.keys(origin).reduce(function(c,k){c[k]=origin[k];return c;},{}); console.log(origin, copy); console.log(origin == copy); // false console.log(origin.foo == copy.foo); // true 

Copie simple et petite ( ECMAScript 6ème édition , 2015):

 var origin = { foo : {} }; var copy = Object.assign({}, origin); console.log(origin, copy); console.log(origin == copy); // false console.log(origin.foo == copy.foo); // true 
20
05 июля '12 в 0:44 2012-07-05 00:44 la réponse est donnée par maël nison le 05 juillet '12 à 12:44 2012-07-05 00:44

Просто потому, что я не видел AngularJS и думал, что люди захотят узнать...

angular.copy также предоставляет метод глубокого копирования объектов и массивов.

17
ответ дан Dan Atkinson 14 мая '16 в 1:16 2016-05-14 01:16