J'ai un objet x
. Je voudrais le copier comme objet y
, de telle sorte que les changements à y
ne pas modifierx
. J'ai réalisé que la copie d'objets dérivés d'objets JavaScript intégrés entraînerait des propriétés supplémentaires et indésirables. Ce n'est pas un problème, car je copie un de mes propres objets construits littéralement.
Comment cloner correctement un objet JavaScript?
javascript
clone
javascript-objects
soundly_typed
la source
la source
mObj=JSON.parse(JSON.stringify(jsonObject));
Object.create(o)
, il fait tout ce que l'auteur demande?var x = { deep: { key: 1 } }; var y = Object.create(x); x.deep.key = 2;
Après cela,y.deep.key
sera également 2, donc Object.create NE PEUT PAS ÊTRE UTILISÉ pour le clonage ...Réponses:
Faire cela pour n'importe quel objet en JavaScript ne sera ni simple ni direct. Vous rencontrerez le problème de la récupération par erreur d'attributs du prototype de l'objet qui doivent être laissés dans le prototype et non copiés dans la nouvelle instance. Si, par exemple, vous ajoutez une
clone
méthodeObject.prototype
, comme le montrent certaines réponses, vous devrez ignorer explicitement cet attribut. Mais que se passe-t-il s'il y a d'autres méthodes supplémentaires ajoutéesObject.prototype
ou d'autres prototypes intermédiaires que vous ne connaissez pas? Dans ce cas, vous copiez les attributs que vous ne devriez pas, vous devez donc détecter les attributs non locaux imprévus avec lahasOwnProperty
méthode.En plus des attributs non énumérables, vous rencontrerez un problème plus difficile lorsque vous essayez de copier des objets qui ont des propriétés masquées. Par exemple,
prototype
est une propriété cachée d'une fonction. De plus, le prototype d'un objet est référencé avec l'attribut__proto__
, qui est également masqué, et ne sera pas copié par une boucle for / in itérant sur les attributs de l'objet source. Je pense que cela__proto__
peut être spécifique à l'interpréteur JavaScript de Firefox et que cela peut être quelque chose de différent dans d'autres navigateurs, mais vous obtenez l'image. Tout n'est pas énumérable. Vous pouvez copier un attribut caché si vous connaissez son nom, mais je ne connais aucun moyen de le découvrir automatiquement.Un autre problème dans la recherche d'une solution élégante est le problème de la configuration correcte de l'héritage du prototype. Si le prototype de votre objet source est
Object
, alors créer simplement un nouvel objet général avec{}
fonctionnera, mais si le prototype de la source est un descendant deObject
, alors vous allez manquer les membres supplémentaires de ce prototype que vous avez sauté en utilisant lehasOwnProperty
filtre, ou qui étaient dans le prototype, mais n'étaient pas énumérables en premier lieu. Une solution peut être d'appeler laconstructor
propriété de l'objet source pour obtenir l'objet de copie initial, puis de copier les attributs, mais vous n'obtiendrez toujours pas d'attributs non énumérables. Par exemple, unDate
objet stocke ses données en tant que membre masqué:La chaîne de date pour
d1
aura 5 secondes de retard sur celle ded2
. Un moyen de rendre l'unDate
identique à l'autre consiste à appeler lasetTime
méthode, mais cela est spécifique à laDate
classe. Je ne pense pas qu'il existe une solution générale pare-balles à ce problème, même si je serais heureux de me tromper!Quand je devais mettre en œuvre profonde générale copie j'ai fini par compromettre en supposant que je seulement besoin de copier une plaine
Object
,Array
,Date
,String
,Number
ouBoolean
. Les 3 derniers types sont immuables, donc je pourrais effectuer une copie superficielle et ne pas m'inquiéter de son changement. J'ai en outre supposé que tous les éléments contenus dansObject
ouArray
seraient également l'un des 6 types simples de cette liste. Cela peut être accompli avec du code comme celui-ci:La fonction ci-dessus fonctionnera correctement pour les 6 types simples que j'ai mentionnés, tant que les données dans les objets et les tableaux forment une structure arborescente. Autrement dit, il n'y a pas plus d'une référence aux mêmes données dans l'objet. Par exemple:
Il ne sera pas en mesure de gérer n'importe quel objet JavaScript, mais il peut être suffisant à de nombreuses fins tant que vous ne supposez pas qu'il fonctionnera uniquement pour tout ce que vous lui lancerez.
la source
JSON.parse(JSON.stringify([some object]),[some revirer function])
une solution?var cpy = new obj.constructor()
?Si vous n'utilisez pas
Date
s, les fonctions, undefined, regExp ou Infinity au sein de votre objet, un simple liner estJSON.parse(JSON.stringify(object))
:Cela fonctionne pour toutes sortes d'objets contenant des objets, des tableaux, des chaînes, des booléens et des nombres.
Voir également cet article sur l'algorithme de clonage structuré des navigateurs utilisé lors de la publication de messages vers et depuis un travailleur. Il contient également une fonction de clonage en profondeur.
source
Avec jQuery, vous pouvez copier superficiellement avec extend :
les modifications ultérieures de la
copiedObject
n'affecteront pas laoriginalObject
et vice versa.Ou pour faire une copie complète :
source
var copiedObject = jQuery.extend({},originalObject);
jQuery.extend(true, {}, originalObject);
...subsequent changes to the copiedObject will not affect the originalObject, and vice versa...
. Désolé d'avoir été vraiment confus.Dans ECMAScript 6, il existe une méthode Object.assign , qui copie les valeurs de toutes les propriétés propres énumérables d'un objet à un autre. Par exemple:
Mais sachez que les objets imbriqués sont toujours copiés comme référence.
source
Object.assign
c'est la voie à suivre. Il est également facile de le remplirPar MDN :
Object.assign({}, a)
JSON.parse(JSON.stringify(a))
Il n'y a pas besoin de bibliothèques externes mais vous devez d'abord vérifier la compatibilité du navigateur .
source
clone
fonctionnalités. voir plus ici momentjs.com/docs/#/parsing/moment-cloneIl existe de nombreuses réponses, mais aucune ne mentionne Object.create d'ECMAScript 5, qui ne vous donne certes pas une copie exacte, mais définit la source comme le prototype du nouvel objet.
Ainsi, ce n'est pas une réponse exacte à la question, mais c'est une solution monoligne et donc élégante. Et cela fonctionne mieux pour 2 cas:
Exemple:
Pourquoi est-ce que je considère cette solution comme supérieure? C'est natif, donc pas de boucle, pas de récursivité. Cependant, les anciens navigateurs auront besoin d'un polyfill.
source
var a = {b:'hello',c:{d:'world'}}, b = Object.create(a); a == b /* false */; a.c == b.c /* true */;
Object.create
pointe vers les propriétés du parent par le biais de références. Cela signifie que si les valeurs des propriétés du parent changent, l'enfant changera également. Cela a des effets secondaires surprenants avec des tableaux et des objets imbriqués qui pourraient entraîner des bogues difficiles à trouver dans votre code si vous ne les connaissez pas: jsbin.com/EKivInO/2 . Un objet cloné est un tout nouvel objet indépendant qui a les mêmes propriétés et valeurs que le parent, mais n'est pas connecté au parent.Une façon élégante de cloner un objet Javascript dans une ligne de code
Une
Object.assign
méthode fait partie de la norme ECMAScript 2015 (ES6) et fait exactement ce dont vous avez besoin.Lire la suite...
Le polyfill pour supporter les anciens navigateurs:
source
Object.assign
prend deux paramètres alors que lavalue
fonction dans le polyfill ne prend qu'un seul paramètre?arguments
objet auparavant.) J'ai du mal à trouverObject()
via Google ... c'est un transtypage, n'est-ce pas?Il existe plusieurs problèmes avec la plupart des solutions sur Internet. J'ai donc décidé de faire un suivi, qui comprend, pourquoi la réponse acceptée ne devrait pas être acceptée.
situation de départ
Je veux copier en profondeur un Javascript
Object
avec tous ses enfants et leurs enfants et ainsi de suite. Mais puisque je ne suis pas une sorte de développeur normale, monObject
a la normaleproperties
,circular structures
et mêmenested objects
.Créons donc un
circular structure
et unnested object
premier.Rassemblons tout dans un
Object
noma
.Ensuite, nous voulons copier
a
dans une variable nomméeb
et la muter.Vous savez ce qui s'est passé ici parce que sinon vous n'atterririez même pas sur cette grande question.
Maintenant, trouvons une solution.
JSON
La première tentative que j'ai essayée a été d'utiliser
JSON
.Ne perdez pas trop de temps dessus, vous en aurez
TypeError: Converting circular structure to JSON
.Copie récursive (la "réponse" acceptée)
Jetons un œil à la réponse acceptée.
Ça a l'air bien, hein? C'est une copie récursive de l'objet et gère également d'autres types
Date
, mais ce n'était pas une exigence.Récursivité et
circular structures
ne fonctionne pas bien ensemble ...RangeError: Maximum call stack size exceeded
solution native
Après avoir discuté avec mon collègue, mon patron nous a demandé ce qui s'était passé et il a trouvé une solution simple après quelques recherches sur Google. Ça s'appelle
Object.create
.Cette solution a été ajoutée à Javascript il y a quelque temps et gère même
circular structure
.... et vous voyez, cela ne fonctionnait pas avec la structure imbriquée à l'intérieur.
polyfill pour la solution native
Il y a un polyfill pour
Object.create
l'ancien navigateur, tout comme IE 8. C'est quelque chose comme recommandé par Mozilla, et bien sûr, ce n'est pas parfait et entraîne le même problème que la solution native .J'ai mis en
F
dehors du champ d'application afin que nous puissions voir ce quiinstanceof
nous dit.Même problème que la solution native , mais une sortie un peu moins bonne.
la meilleure solution (mais pas parfaite)
En fouillant, j'ai trouvé une question similaire ( en Javascript, lors de l'exécution d'une copie complète , comment puis-je éviter un cycle, car une propriété est "this"? ) À celle-ci, mais avec une bien meilleure solution.
Et regardons la sortie ...
Les exigences sont identiques, mais il y a encore quelques petits problèmes, y compris la modification
instance
dunested
etcirc
duObject
.conclusion
La dernière solution utilisant la récursivité et un cache n'est peut-être pas la meilleure, mais c'est une véritable copie en profondeur de l'objet. Il gère simple
properties
,circular structures
etnested object
, mais il gâchera leur instance lors du clonage.jsfiddle
source
Si vous êtes d'accord avec une copie superficielle, la bibliothèque underscore.js a une méthode de clonage .
ou vous pouvez l'étendre comme
source
OK, imaginez que vous avez cet objet ci-dessous et que vous souhaitez le cloner:
ou
la réponse est depeneds principalement sur laquelle EcmaScript vous utilisez, dans
ES6+
, vous pouvez simplement utiliserObject.assign
pour faire le clone:ou en utilisant un opérateur de diffusion comme celui-ci:
Mais si vous utilisez
ES5
, vous pouvez utiliser quelques méthodes, maisJSON.stringify
assurez-vous de ne pas utiliser pour un gros morceau de données à copier, mais cela pourrait être une ligne pratique dans de nombreux cas, quelque chose comme ceci:source
big chunk of data
cela équivaudrait? 100kb? 100 Mo? Merci!Object.assign
fait une copie superficielle (tout comme la propagation, @Alizera)Une solution particulièrement inélégante consiste à utiliser le codage JSON pour créer des copies complètes d'objets qui n'ont pas de méthodes membres. La méthodologie consiste à encoder JSON votre objet cible, puis en le décodant, vous obtenez la copie que vous recherchez. Vous pouvez décoder autant de fois que vous le souhaitez pour faire autant de copies que vous le souhaitez.
Bien sûr, les fonctions n'appartiennent pas à JSON, donc cela ne fonctionne que pour les objets sans méthodes membres.
Cette méthodologie était parfaite pour mon cas d'utilisation, car je stocke des objets JSON dans un magasin de valeurs-clés, et lorsqu'ils sont exposés en tant qu'objets dans une API JavaScript, chaque objet contient en fait une copie de l'état d'origine de l'objet afin que nous peut calculer le delta après que l'appelant a muté l'objet exposé.
source
{ 'foo': function() { return 1; } }
est un objet construit littéralement.Vous pouvez simplement utiliser une propriété spread pour copier un objet sans références. Mais attention (voir commentaires), la «copie» est juste au niveau objet / tableau le plus bas. Les propriétés imbriquées sont toujours des références!
Clone complet:
Clone avec références au deuxième niveau:
JavaScript ne prend pas en charge nativement les clones profonds. Utilisez une fonction utilitaire. Par exemple Ramda:
source
const first = {a: 'foo', b: 'bar'}; const second = {...{a} = first}
Pour ceux qui utilisent AngularJS, il existe également une méthode directe pour le clonage ou l'extension des objets dans cette bibliothèque.
ou
Plus dans la documentation angular.copy ...
source
Voici une fonction que vous pouvez utiliser.
source
new
. La réponse acceptée ne le fait pas.La réponse d'A. Levy est presque complète, voici ma petite contribution: il y a un moyen de gérer les références récursives , voir cette ligne
if(this[attr]==this) copy[attr] = copy;
Si l'objet est un élément DOM XML, nous devons utiliser cloneNode à la place
if(this.cloneNode) return this.cloneNode(true);
Inspiré par l'étude approfondie d'A.Levy et l'approche de prototypage de Calvin, je propose cette solution:
Voir aussi la note d'Andy Burke dans les réponses.
source
Date.prototype.clone = function() {return new Date(+this)};
De cet article: Comment copier des tableaux et des objets en Javascript par Brian Huisman:
source
var copiedObj = Object.create(obj);
un excellent moyen?Dans ES-6, vous pouvez simplement utiliser Object.assign (...). Ex:
Une bonne référence est ici: https://googlechrome.github.io/samples/object-assign-es6/
source
let obj = {person: 'Thor Odinson'}; let clone = Object.assign({}, obj); clone.title = "Whazzup";
)obj
propriétés sont en effet clonées. Les valeurs de propriété qui sont des objets eux-mêmes ne seront pas clonées.Dans ECMAScript 2018
N'oubliez pas que les objets imbriqués sont toujours copiés comme référence.
source
const objDeepClone = JSON.parse(JSON.stringify(obj));
Utilisation de Lodash:
source
_.cloneDeep(x)
car c'est essentiellement la même chose que ci-dessus, mais lit mieux.Intéressé par le clonage d'objets simples:
JSON.parse(JSON.stringify(json_original));
Source: Comment copier un objet JavaScript dans une nouvelle variable NON par référence?
source
Vous pouvez cloner un objet et supprimer toute référence de l'ancien à l'aide d'une seule ligne de code. Faites simplement:
Pour les navigateurs / moteurs qui ne prennent actuellement pas en charge Object.create, vous pouvez utiliser ce polyfill:
source
Object.create(...)
semble définitivement la voie à suivre.Object.hasOwnProperty
? De cette façon, les gens savent comment empêcher la recherche du lien prototype.text
observez le membre dans obj2. Vous ne faites pas de copie, vous vous contentez de vous reporter à la chaîne de prototype lorsqu'un membre n'est pas trouvé sur obj2.Nouvelle réponse à une vieille question! Si vous avez le plaisir d'utiliser ECMAScript 2016 (ES6) avec Spread Syntax , c'est facile.
Cela fournit une méthode propre pour une copie superficielle d'un objet. Faire une copie profonde, ce qui signifie créer une nouvelle copie de chaque valeur dans chaque objet imbriqué récursivement, nécessite l'une des solutions les plus lourdes ci-dessus.
JavaScript continue d'évoluer.
source
var obj = {'key1': 'value1'};
var array = [...obj]; // TypeError: obj is not iterable
Solution ES6 si vous voulez cloner (superficiellement) une instance de classe et pas seulement un objet de propriété.
la source
let cloned = Object.assign({}, obj)
?Je pense qu'il existe une réponse simple et efficace. Dans la copie profonde, il y a deux préoccupations:
Je pense donc qu'une solution simple sera d'abord de sérialiser et de désérialiser puis de faire une affectation dessus pour copier des fonctions aussi.
Bien que cette question ait de nombreuses réponses, j'espère que celle-ci vous aidera aussi.
la source
cloneDeep
.Pour une copie complète et un clone, JSON.stringify puis JSON.parse l'objet:
la source
Ceci est une adaptation du code de A. Levy pour gérer également le clonage de fonctions et de références multiples / cycliques - ce que cela signifie, c'est que si deux propriétés dans l'arbre qui est cloné sont des références du même objet, l'arbre d'objet cloné aura ces Les propriétés pointent vers un seul et même clone de l'objet référencé. Cela résout également le cas des dépendances cycliques qui, si elles ne sont pas gérées, conduisent à une boucle infinie. La complexité de l'algorithme est O (n)
Quelques tests rapides
la source
Je voulais juste ajouter à tous les
Object.create
solutions de ce post, que cela ne fonctionne pas de la manière souhaitée avec nodejs.Dans Firefox, le résultat de
est
{test:"test"}
.Dans nodejs c'est
la source
Object.hasOwnProperty
pour vérifier si vous possédez le tableau ou non. Oui, cela ajoute une complexité supplémentaire pour gérer l'héritage prototypique.la source
if(!src && typeof src != "object"){
. Je pense que cela ne devrait||
pas l' être&&
.Depuis mindeavour déclaré que l'objet à cloner est un objet `` construit littéralement '', une solution pourrait être de simplement générer l'objet plusieurs fois plutôt que de cloner une instance de l'objet:
la source
J'ai écrit ma propre implémentation. Je ne sais pas si cela compte comme une meilleure solution:
Voici la mise en œuvre:
la source