Cette page a été traduite à partir de l'anglais par la communauté. Vous pouvez contribuer en rejoignant la communauté francophone sur MDN Web Docs.

View in English Always switch to English

Copie profonde

Une copie profonde (deep copy en anglais) d'un objet est une copie dont les propriétés ne partagent pas les mêmes références (ne pointent pas vers les mêmes valeurs sous-jacentes) que celles de l'objet source à partir duquel la copie a été faite. Par conséquent, lorsque vous modifiez soit la source, soit la copie, vous pouvez être assuré·e que vous ne faites pas changer l'autre objet. Ce comportement contraste avec celui d'une copie superficielle, dans lequel les modifications des propriétés imbriquées dans la source ou la copie peuvent également faire changer l'autre objet.

Une copie profonde est généralement implémentée de manière récursive comme suit :

  1. Un nouvel objet du même type est créé. La chaîne de prototypes peut ou pas être copiée (et généralement ce n'est pas le cas), mais par exemple, une copie profonde d'un tableau (Array) doit donner un tableau (Array) et pas autre chose.
  2. Pour chaque propriété propre de l'objet original, une propriété avec la même clé et les mêmes descripteurs est définie sur le nouvel objet.
  3. La valeur de chaque nouvelle propriété est définie comme une copie profonde de la valeur de la propriété originale. Si la valeur de la propriété est un type primitif, aucune copie n'est effectuée.
  4. Toute donnée qui n'est pas exposée en tant que propriété (comme Map) est copiée en profondeur, à condition que l'implémentation reconnaisse le type d'objet et sache comment récupérer et définir les données.
  5. En général, il existe un certain support pour les références circulaires.

Notez que comme JavaScript n'a pas de mécanisme intégré qui effectue une véritable copie profonde, les implémentations des bibliothèques divergent souvent sur des détails techniques, tels que :

  • Si les propriétés non énumérables ou symboliques sont copiées
  • Si les propriétés d'accesseur sont copiées en tant qu'accesseurs
  • Si les propriétés du prototype sont copiées
  • Quelles structures de données prennent en charge la copie des données qui ne sont pas des propriétés

La copie d'un objet dont toutes les propriétés ont des valeurs primitives correspond à la définition d'une copie profonde et d'une copie superficielle. Il est quelque peu inutile de parler de la profondeur d'une telle copie, car elle n'a pas de propriétés imbriquées et nous parlons généralement de copie profonde dans le contexte de la modification de propriétés imbriquées.

En JavaScript, les opérations standards intégrées de copie d'objet (syntaxe de propagation, Array.prototype.concat(), Array.prototype.slice(), Array.from(), et Object.assign()) ne créent pas de copies profondes (elles créent des copies superficielles).

Une façon de faire une copie profonde d'un objet JavaScript, s'il peut être sérialisé, est d'utiliser JSON.stringify() pour convertir l'objet en une chaîne de caractères JSON, puis JSON.parse() pour convertir la chaîne de caractères en un nouvel objet JavaScript (complètement nouveau) :

js
const listeDesIngedients = ["pâtes", { list: ["oeufs", "farine", "eau"] }];
const copieProfondeListeDesIngredients = JSON.parse(
  JSON.stringify(listeDesIngedients),
);

Comme une copie profonde ne partage aucune référence avec son objet source, toute modification apportée à la copie profonde n'affecte pas l'objet source.

js
// On change la valeur de la propriété 'list' dans copieProfondeListeDesIngredients.
copieProfondeListeDesIngredients[1].list = ["farine de riz", "eau"];
// La propriété 'list' ne change pas dans listeDesIngedients.
console.log(listeDesIngedients[1].list);
// Array(3) [ "oeufs", "farine", "eau" ]

Cependant, bien que l'objet dans le code ci-dessus soit suffisamment simple pour être sérialisé, de nombreux objets JavaScript ne sont pas possibles à sérialiser du tout — par exemple, les fonctions (avec des fermetures), les Symboles, les objets qui représentent des éléments HTML dans l'API DOM HTML, les données récursives, et bien d'autres cas. Appeler JSON.stringify() pour sérialiser les objets dans ces cas échoue. Faire une copie profonde de ces objets nécessite d'autres API ou bibliothèques.

L'API web structuredClone() crée également des copies profondes et a l'avantage de permettre aux objets transférables dans la source d'être transférés vers la nouvelle copie, plutôt que simplement clonés. Elle gère également plus de types de données, comme Error. Mais notez que structuredClone() n'est pas une fonctionnalité du langage JavaScript lui-même — c'est une fonctionnalité des navigateurs et autres hôtes JavaScript qui implémentent les API web. Et appeler structuredClone() pour cloner un objet non sérialisable échoue de la même manière qu'appeler JSON.stringify() pour le sérialiser.

Voir aussi