Quelle est la façon la plus efficace de cloner un objet JavaScript? J'ai vu obj = eval(uneval(o));
être utilisé, mais ce n'est pas standard et n'est pris en charge que par Firefox .
J'ai fait des choses comme obj = JSON.parse(JSON.stringify(o));
mais remettre en question l'efficacité.
J'ai également vu des fonctions de copie récursives avec divers défauts.
Je suis surpris qu'aucune solution canonique n'existe.
javascript
object
clone
jschrab
la source
la source
eval()
est généralement une mauvaise idée car de nombreux optimiseurs de moteur Javascript doivent être désactivés lorsqu'ils traitent des variables définies viaeval
. Le simple fait d'avoireval()
dans votre code peut conduire à de moins bonnes performances.JSON
méthode perdra tous les types Javascript qui n'ont pas d'équivalent dans JSON. Par exemple:JSON.parse(JSON.stringify({a:null,b:NaN,c:Infinity,d:undefined,e:function(){},f:Number,g:false}))
générera{a: null, b: null, c: null, g: false}
Réponses:
Clonage profond natif
Cela s'appelle le "clonage structuré", fonctionne expérimentalement dans Node 11 et versions ultérieures et, espérons-le, atterrira dans les navigateurs. Voir cette réponse pour plus de détails.
Clonage rapide avec perte de données - JSON.parse / stringify
Si vous n'utilisez pas
Date
s, fonctionsundefined
,Infinity
expressions rationnelles, Cartes, Ensembles, Blobs, filelists, ImageDatas, tableaux rares, tableaux dactylographié ou d' autres types complexes au sein de votre objet, une doublure très simple clone profond d' un objet est:JSON.parse(JSON.stringify(object))
Voir la réponse de Corban pour les repères.
Clonage fiable à l'aide d'une bibliothèque
Étant donné que le clonage d'objets n'est pas trivial (types complexes, références circulaires, fonction, etc.), la plupart des bibliothèques principales fournissent une fonction pour cloner des objets. Ne réinventez pas la roue - si vous utilisez déjà une bibliothèque, vérifiez si elle a une fonction de clonage d'objet. Par exemple,
cloneDeep
; peut être importé séparément via le lodash.clonedeep module et est probablement votre meilleur choix si vous n'utilisez pas déjà une bibliothèque qui fournit une fonction de clonage en profondeurangular.copy
jQuery.extend(true, { }, oldObject)
;.clone()
clone uniquement les éléments DOMES6
Pour être complet, notez que ES6 propose deux mécanismes de copie superficielle:
Object.assign()
et la syntaxe de propagation . qui copie les valeurs de toutes les propriétés propres énumérables d'un objet à un autre. Par exemple:la source
.clone()
, ce qui n'est pas le bon code à utiliser) dans ce contexte). Malheureusement, cette question a subi tant de révisions que la discussion originale n'est même plus apparente! Veuillez simplement suivre les conseils de Corban et écrire une boucle ou copier les propriétés directement sur un nouvel objet, si vous vous souciez de la vitesse. Ou testez-le par vous-même!Découvrez cette référence: http://jsben.ch/#/bWfk9
Lors de mes tests précédents où la vitesse était une préoccupation majeure, j'ai trouvé
pour être le moyen le plus lent de cloner en profondeur un objet (il est plus lent que jQuery.extend avec un
deep
indicateur défini sur 10-20%).jQuery.extend est assez rapide lorsque l'
deep
indicateur est défini surfalse
(clone superficiel). C'est une bonne option, car elle inclut une logique supplémentaire pour la validation de type et ne copie pas les propriétés non définies, 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
for (var i in obj)
boucle simple pour cloner votre objet tout en vérifiant hasOwnProperty et ce sera beaucoup plus rapide que jQuery.Enfin, si vous essayez de cloner une structure d'objet connue dans une boucle chaude, vous pouvez obtenir BEAUCOUP PLUS DE PERFORMANCES en insérant simplement la procédure de clonage et en construisant manuellement l'objet.
Les moteurs de trace JavaScript sont
for..in
optimistes pour l'optimisation des boucles et la vérification de hasOwnProperty vous ralentira également. Clonage manuel lorsque la vitesse est un must absolu.Attention à utiliser la
JSON.parse(JSON.stringify(obj))
méthode sur lesDate
objets -JSON.stringify(new Date())
retourne une représentation sous forme de chaîne de la date au format ISO, quiJSON.parse()
ne se reconvertit pas enDate
objet. Voir cette réponse pour plus de détails .De plus, veuillez noter que, dans Chrome 65 au moins, le clonage natif n'est pas la solution. Selon JSPerf, effectuer un clonage natif en créant une nouvelle fonction est presque 800x plus lent que d'utiliser JSON.stringify qui est incroyablement rapide sur toute la ligne.
Mise à jour pour ES6
Si vous utilisez Javascript ES6, essayez cette méthode native de clonage ou de copie superficielle.
la source
keys
de votreobject
, qui afunctions
pour valeur, car leJSON
ne prend pas en charge les fonctions.JSON.parse(JSON.stringify(obj))
d'objets de date convertira également la date en UTC dans la représentation sous forme de chaîne au format ISO8601 .En supposant que vous n'avez que des variables et pas de fonctions dans votre objet, vous pouvez simplement utiliser:
la source
JSON
est implémenté en code natif (dans la plupart des navigateurs), cela sera considérablement plus rapide que l'utilisation de toute autre solution de copie profonde basée sur javascript, et peut parfois être plus rapide qu'une technique de copie superficielle basée sur javascript (voir: jsperf.com/cloning -un-objet / 79 ).JSON.stringify({key: undefined}) //=> "{}"
Date
objets qui sont stockés à l'intérieur de l'objet, en les convertissant sous forme de chaîne.Clonage structuré
La norme HTML comprend un algorithme de clonage / sérialisation structuré interne qui peut créer des clones profonds d'objets. Il est toujours limité à certains types intégrés, mais en plus des quelques types pris en charge par JSON, il prend également en charge les dates, les RegExps, les cartes, les ensembles, les objets blob, les listes de fichiers, les imagesDatas, les tableaux clairsemés, les tableaux typés et probablement plus à l'avenir . Il préserve également les références dans les données clonées, ce qui lui permet de prendre en charge des structures cycliques et récursives qui provoqueraient des erreurs pour JSON.
Prise en charge dans Node.js: expérimental 🙂
Le
v8
module dans Node.js actuellement (à partir de Node 11) expose directement l'API de sérialisation structurée , mais cette fonctionnalité est toujours marquée comme "expérimentale" et sujette à modification ou suppression dans les futures versions. Si vous utilisez une version compatible, le clonage d'un objet est aussi simple que:Prise en charge directe dans les navigateurs: peut-être éventuellement? 😐
Les navigateurs ne fournissent pas actuellement d'interface directe pour l'algorithme de clonage structuré, mais une
structuredClone()
fonction globale a été discutée dans whatwg / html # 793 sur GitHub . Tel qu'il est actuellement proposé, son utilisation dans la plupart des cas serait aussi simple que:Sauf si cela est livré, les implémentations de clones structurés des navigateurs ne sont exposées qu'indirectement.
Solution de contournement asynchrone: utilisable. 😕
La manière la plus simple de créer un clone structuré avec des API existantes consiste à publier les données via un port d'un MessageChannels . L'autre port émettra un
message
événement avec un clone structuré de l'attaché.data
. Malheureusement, l'écoute de ces événements est nécessairement asynchrone et les alternatives synchrones sont moins pratiques.Exemple d'utilisation:
Solutions de contournement synchrones: horribles! 🤢
Il n'y a pas de bonnes options pour créer des clones structurés de manière synchrone. Voici quelques astuces peu pratiques à la place.
history.pushState()
et leshistory.replaceState()
deux créent un clone structuré de leur premier argument et attribuent cette valeur àhistory.state
. Vous pouvez l'utiliser pour créer un clone structuré de n'importe quel objet comme celui-ci:Exemple d'utilisation:
Afficher l'extrait de code
Bien que synchrone, cela peut être extrêmement lent. Il entraîne tous les frais généraux associés à la manipulation de l'historique du navigateur. L'appel répété de cette méthode peut entraîner une absence temporaire de réponse de Chrome.
Le
Notification
constructeur crée un clone structuré de ses données associées. Il tente également d'afficher une notification du navigateur à l'utilisateur, mais cela échouera en silence, sauf si vous avez demandé l'autorisation de notification. Si vous avez l'autorisation à d'autres fins, nous fermerons immédiatement la notification que nous avons créée.Exemple d'utilisation:
Afficher l'extrait de code
la source
history.pushState()
et sonthistory.replaceState()
toutes deux synchroniséeshistory.state
sur un clone structuré de leur premier argument. Un peu bizarre, mais ça marche. Je mets à jour ma réponse maintenant.S'il n'y en avait pas, vous pouvez essayer:
la source
Le moyen efficace de cloner (pas de cloner en profondeur) un objet sur une seule 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:
la source
Code:
Tester:
la source
var obj = {}
obj.a = obj
from.constructor
c'estDate
par exemple. Comment le troisièmeif
test serait-il atteint lorsque le deuxièmeif
test réussirait et entraînerait le retour de la fonction (depuisDate != Object && Date != Array
)?Voici ce que j'utilise:
la source
cloneObject({ name: null })
=>{"name":{}}
typeof null > "object"
maisObject.keys(null) > TypeError: Requested keys of a value that is not an object.
changez la condition enif(typeof(obj[i])=="object" && obj[i]!=null)
Copie approfondie par performance: classée du meilleur au pire
Copie complète d'un tableau de chaînes ou de nombres (un niveau - pas de pointeurs de référence):
Lorsqu'un tableau contient des nombres et des chaînes - des fonctions comme .slice (), .concat (), .splice (), l'opérateur d'affectation "=" et la fonction clone d'Underscore.js; fera une copie complète des éléments du tableau.
Là où la réaffectation a les performances les plus rapides:
Et .slice () a de meilleures performances que .concat (), http://jsperf.com/duplicate-array-slice-vs-concat/3
Copie en profondeur d'un tableau d'objets (deux niveaux ou plus - pointeurs de référence):
Écrivez une fonction personnalisée (a des performances plus rapides que $ .extend () ou JSON.parse):
Utilisez des fonctions utilitaires tierces:
Où $ .extend de jQuery a de meilleures performances:
la source
out[key] = (typeof v === "object" && v !== null) ? copy(v) : v;
la source
Copie en profondeur d'objets en JavaScript (je pense que le meilleur et le plus simple)
1. Utilisation de JSON.parse (JSON.stringify (object));
2.Utilisation de la méthode créée
3. Utilisation du lodash de lien _.cloneDeep de Lo-Dash
4. Utilisation de la méthode Object.assign ()
MAIS MAUVAIS QUAND
5.Utilisation du lien Underscore.js _.clone Underscore.js
MAIS MAUVAIS QUAND
JSBEN.CH Performance Benchmarking Playground 1 ~ 3 http://jsben.ch/KVQLd
la source
Object.assign()
n'effectue pas de copielet data = {title:["one", "two"]}; let tmp = cloneObject(data); tmp.title.pop();
il jette:TypeError: tmp.title.pop is not a function
(bien sûr pop () fonctionne bien si je viensdo let tmp = data
; mais alors je ne peux pas modifier tmp sans affecter les données)Il y a une bibliothèque (appelée "clone") , qui fait cela très bien. Il fournit le clonage / copie récursif le plus complet d'objets arbitraires que je connaisse. Il prend également en charge les références circulaires, qui ne sont pas encore couvertes par les autres réponses.
Vous pouvez également le trouver sur npm . Il peut être utilisé pour le navigateur ainsi que Node.js.
Voici un exemple sur la façon de l'utiliser:
Installez-le avec
ou emballez-le avec Ender .
Vous pouvez également télécharger le code source manuellement.
Ensuite, vous pouvez l'utiliser dans votre code source.
(Avertissement: je suis l'auteur de la bibliothèque.)
la source
JSON.parse(JSON.stringify(obj))
?Cloning
un objet a toujours été un problème dans JS, mais c'était avant ES6, je liste ci-dessous différentes façons de copier un objet en JavaScript, imaginez que vous avez l'objet ci-dessous et que vous souhaitez en avoir une copie complète:Il existe plusieurs façons de copier cet objet sans modifier son origine:
1) ES5 +, en utilisant une fonction simple pour faire la copie pour vous:
2) ES5 +, en utilisant JSON.parse et JSON.stringify.
3) AngularJs:
4) jQuery:
5) UnderscoreJs & Loadash:
J'espère que ces aides ...
la source
Object.assign
ne copie pas en profondeur. Exemple:var x = { a: { b: "c" } }; var y = Object.assign({}, x); x.a.b = "d"
. Si c'était une copie complète, cey.a.b
serait toujours le casc
, mais c'est maintenantd
.Je sais que c'est un vieux post, mais je pensais que cela pourrait être utile à la prochaine personne qui trébucherait.
Tant que vous n'assignez aucun objet à quelque chose, il ne conserve aucune référence en mémoire. Donc, pour créer un objet que vous souhaitez partager entre d'autres objets, vous devrez créer une usine comme ceci:
la source
const defaultFoo = { a: { b: 123 } };
vous pouvez y allerconst defaultFoo = () => ({ a: { b: 123 } };
et votre problème est résolu. Cependant, ce n'est vraiment pas une réponse à la question. Cela aurait peut-être été plus logique en tant que commentaire sur la question, et non en tant que réponse complète.Si vous l'utilisez, la bibliothèque Underscore.js possède une méthode de clonage .
la source
.clone(...)
méthode d'une bibliothèque d'utilitaires . Chaque grande bibliothèque en aura, et les brèves réponses non détaillées répétées ne sont pas utiles à la plupart des visiteurs, qui n'utiliseront pas cette bibliothèque particulière.Voici une version de la réponse de ConroyP ci-dessus qui fonctionne même si le constructeur a des paramètres requis:
Cette fonction est également disponible dans ma bibliothèque simpleoo .
Éditer:
Voici une version plus robuste (grâce à Justin McCandless, elle prend désormais également en charge les références cycliques):
la source
Ce qui suit crée deux instances du même objet. Je l'ai trouvé et je l'utilise actuellement. C'est simple et facile à utiliser.
la source
Crockford suggère (et je préfère) d'utiliser cette fonction:
C'est concis, fonctionne comme prévu et vous n'avez pas besoin d'une bibliothèque.
ÉDITER:
Il s'agit d'un polyfill pour
Object.create
, vous pouvez donc également l'utiliser.Remarque: si vous utilisez une partie de cela, vous pouvez avoir des problèmes avec certaines itérations qui utilisent
hasOwnProperty
. Parce que,create
créez un nouvel objet vide qui hériteoldObject
. Mais il reste utile et pratique pour cloner des objets.Par exemple si
oldObject.a = 5;
mais:
la source
var extendObj = function(childObj, parentObj) { var tmpObj = function () {} tmpObj.prototype = parentObj.prototype; childObj.prototype = new tmpObj(); childObj.prototype.constructor = childObj; };
... davidshariff.com/blog/javascript-inheritance-patternsLodash a une belle méthode _.cloneDeep (value) :
la source
.clone(...)
méthode d'une bibliothèque d'utilitaires . Chaque grande bibliothèque en aura, et les brèves réponses non détaillées répétées ne sont pas utiles à la plupart des visiteurs, qui n'utiliseront pas cette bibliothèque particulière._.merge({}, objA)
. Si seulement lodash n'a pas muté les objets en premier lieu, alors laclone
fonction ne serait pas nécessaire.la source
Copie simple à une ligne ( ECMAScript 5e édition ):
Et une copie peu profonde ( ECMAScript 6e édition , 2015):
la source
Object.keys
ignore les propriétés non énumérables et héritées. En outre, il perd les descripteurs de propriété en effectuant une affectation directe.Juste parce que je n'ai pas vu AngularJS mentionné et j'ai pensé que les gens pourraient vouloir savoir ...
angular.copy
fournit également une méthode de copie en profondeur d'objets et de tableaux.la source
angular.extend({},obj);
jQuery.extend
etangular.extend
sont à la fois des copies peu profondes.angular.copy
est une copie profonde.Il ne semble pas encore y avoir d'opérateur de clone profond idéal pour les objets de type tableau. Comme l'illustre le code ci-dessous, le cloneur jQuery de John Resig transforme les tableaux avec des propriétés non numériques en objets qui ne sont pas des tableaux, et le cloner JSON de RegDwight supprime les propriétés non numériques. Les tests suivants illustrent ces points sur plusieurs navigateurs:
la source
J'ai deux bonnes réponses selon que votre objectif est de cloner un "ancien objet JavaScript" ou non.
Supposons également que votre intention est de créer un clone complet sans référence de prototype à l'objet source. Si vous n'êtes pas intéressé par un clone complet, vous pouvez utiliser la plupart des routines Object.clone () fournies dans certaines des autres réponses (modèle de Crockford).
Pour les anciens objets JavaScript simples, une bonne façon éprouvée de cloner un objet dans les runtimes modernes est tout simplement:
Notez que l'objet source doit être un objet JSON pur. Cela signifie que toutes ses propriétés imbriquées doivent être scalaires (comme booléen, chaîne, tableau, objet, etc.). Aucune fonction ou objet spécial comme RegExp ou Date ne sera cloné.
Est-ce efficace? Zut oui. Nous avons essayé toutes sortes de méthodes de clonage et cela fonctionne mieux. Je suis sûr que certains ninja pourraient évoquer une méthode plus rapide. Mais je pense que nous parlons de gains marginaux.
Cette approche est simplement simple et facile à mettre en œuvre. Enveloppez-le dans une fonction de commodité et si vous avez vraiment besoin d'extraire du gain, optez pour plus tard.
Maintenant, pour les objets JavaScript non simples, il n'y a pas de réponse vraiment simple. En fait, cela ne peut pas être dû à la nature dynamique des fonctions JavaScript et à l'état de l'objet interne. Le clonage en profondeur d'une structure JSON avec des fonctions à l'intérieur nécessite de recréer ces fonctions et leur contexte interne. Et JavaScript n'a tout simplement pas de façon standardisée de le faire.
La bonne façon de le faire, encore une fois, est via une méthode pratique que vous déclarez et réutilisez dans votre code. La méthode pratique peut être dotée d'une certaine compréhension de vos propres objets afin que vous puissiez vous assurer de recréer correctement le graphique dans le nouvel objet.
Nous sommes nos propres écrits, mais la meilleure approche générale que j'ai vue est couverte ici:
http://davidwalsh.name/javascript-clone
C'est la bonne idée. L'auteur (David Walsh) a commenté le clonage de fonctions généralisées. C'est quelque chose que vous pourriez choisir de faire, selon votre cas d'utilisation.
L'idée principale est que vous devez gérer spécifiquement l'instanciation de vos fonctions (ou classes prototypiques, pour ainsi dire) par type. Ici, il a fourni quelques exemples pour RegExp et Date.
Non seulement ce code est bref, mais il est également très lisible. C'est assez facile à étendre.
Est-ce efficace? Zut oui. Étant donné que le but est de produire un véritable clone de copie profonde, vous devrez parcourir les membres du graphique d'objet source. Avec cette approche, vous pouvez modifier exactement les membres enfants à traiter et comment gérer manuellement les types personnalisés.
Alors voilà. Deux approches. Les deux sont efficaces à mon avis.
la source
Ce n'est généralement pas la solution la plus efficace, mais elle fait ce dont j'ai besoin. Cas de test simples ci-dessous ...
Test de réseau cyclique ...
Test de fonctionnalité...
la source
AngularJS
Eh bien, si vous utilisez angulaire, vous pouvez aussi le faire
la source
Je ne suis pas d'accord avec la réponse avec les plus grands votes ici . Un clone profond récursif est beaucoup plus rapide que l' approche JSON.parse (JSON.stringify (obj)) mentionnée.
Et voici la fonction de référence rapide:
la source
if(o instanceof Date) return new Date(o.valueOf());
après avoir vérifié la valeur null `la source
Uniquement lorsque vous pouvez utiliser ECMAScript 6 ou des transpilers .
Fonctionnalités:
Code:
la source
Voici une méthode clone () complète qui peut cloner n'importe quel objet JavaScript. Il gère presque tous les cas:
la source