Fermer

avril 12, 2023

Copie superficielle ou copie profonde en JavaScript —

Copie superficielle ou copie profonde en JavaScript —


Copier et modifier des objets en JavaScript n’est jamais aussi simple qu’il n’y paraît. Comprendre le fonctionnement des objets et des références au cours de ce processus est essentiel pour les développeurs Web et peut leur faire économiser des heures de débogage. Cela devient de plus en plus important lorsque vous travaillez avec de grandes applications avec état comme celles construites dans React ou Vue.

La copie superficielle et la copie en profondeur font référence à la façon dont nous faisons des copies d’un objet en JavaScript et aux données qui sont créées dans la « copie ». Dans cet article, nous allons approfondir les distinctions entre ces méthodes, explorer leurs applications dans le monde réel et découvrir les pièges potentiels qui peuvent survenir lors de leur utilisation.

Qu’est-ce que la copie « superficielle »

La copie superficielle fait référence au processus de création d’un nouvel objet qui est une copie d’un objet existant, avec ses propriétés faisant référence aux mêmes valeurs ou objets que l’original. En JavaScript, cela est souvent réalisé en utilisant des méthodes telles que Object.assign() ou la syntaxe de propagation ({...originalObject}). La copie superficielle crée uniquement une nouvelle référence aux objets ou valeurs existants et ne crée pas de copie complète, ce qui signifie que les objets imbriqués sont toujours référencés et non dupliqués.

Regardons l’exemple de code suivant. L’objet nouvellement créé shallowCopyZoo est créé en tant que copie de zoo via l’opérateur de propagation, ce qui a entraîné des conséquences imprévues.

let zoo = {
  name: "Amazing Zoo",
  location: "Melbourne, Australia",
  animals: [
    {
      species: "Lion",
      favoriteTreat: "🥩",
    },
    {
      species: "Panda",
      favoriteTreat: "🎋",
    },
  ],
};

let shallowCopyZoo = { ...zoo };
shallowCopyZoo.animals[0].favoriteTreat = "🍖";
console.log(zoo.animals[0].favoriteTreat); 
// "🍖", not "🥩"

Mais regardons ce qu’il y a vraiment dans shallowCopyZoo. Les propriétés name et location sont des valeurs primitives (chaîne), donc leurs valeurs sont copiées. Cependant, le animals La propriété est un tableau d’objets, donc la référence à ce tableau est copiée, pas le tableau lui-même.

Vous pouvez rapidement tester cela (si vous ne me croyez pas) en utilisant le opérateur d’égalité stricte (===). Un objet n’est égal à un autre objet que si le se référer à au même objet (voir Types de données primitifs et types de référence). Remarquez comment la propriété animals est égal sur les deux mais les objets eux-mêmes ne sont pas égaux.

console.log(zoo.animals === shallowCopyZoo.animals)
// true

console.log(zoo === shallowCopyZoo)
// false

Cela peut entraîner des problèmes potentiels dans les bases de code et rendre la vie particulièrement difficile lorsque vous travaillez avec de gros objets. La modification d’un objet imbriqué dans la copie superficielle affecte également l’objet d’origine et toute autre copie superficielle, car ils partagent tous la même référence.

Copie en profondeur

La copie en profondeur est une technique qui crée un nouvel objet, qui est une copie exacte d’un objet existant. Ceci comprend copier toutes ses propriétés et tous les objets imbriqués, au lieu des références. Le clonage en profondeur est utile lorsque vous avez besoin de deux objets distincts qui ne partagent pas de références, garantissant que les modifications apportées à un objet n’affectent pas l’autre.

Les programmeurs utilisent souvent le clonage en profondeur lorsqu’ils travaillent avec des objets d’état d’application dans des applications complexes. La création d’un nouvel objet d’état sans affecter l’état précédent est cruciale pour maintenir la stabilité de l’application et implémenter correctement la fonctionnalité d’annulation et de rétablissement.

Comment copier en profondeur en utilisant JSON.stringify() et JSON.parse()

Un moyen populaire et sans bibliothèque de copier en profondeur consiste à utiliser le JSON stringify() et parse() méthodes.

Le La méthode parse(stringify()) n’est pas parfaite. Par exemple, des types de données spéciaux comme Date sera stringifié et undefined les valeurs seront ignorées. Comme toutes les options de cet article, elle doit être considérée pour votre cas d’utilisation individuel.

Dans le code ci-dessous, nous allons créer un deepCopy utiliser ces méthodes pour cloner en profondeur un objet. Nous copions ensuite le playerProfile objet et modifier l’objet copié sans affecter l’original. Cela montre la valeur de la copie en profondeur dans le maintien d’objets séparés sans références partagées.

const playerProfile = {
  name: 'Alice',
  level: 10,
  achievements: [
    {
      title: 'Fast Learner',
      emoji: '🚀'
    },
    {
      title: 'Treasure Hunter',
      emoji: '💰'
    }
  ]
};

function deepCopy(obj) {
  return JSON.parse(JSON.stringify(obj));
}

const clonedProfile = deepCopy(playerProfile);

console.log(clonedProfile);
/* Output:
{
  name: 'Alice',
  level: 10,
  achievements: [
    {
      title: 'Fast Learner',
      emoji: '🚀'
    },
    {
      title: 'Treasure Hunter',
      emoji: '💰'
    }
  ]
}
*/

// Modify the cloned profile without affecting the original profile
clonedProfile.achievements.push({ title: 'Marathon Runner', emoji: '🏃' });
console.log(playerProfile.achievements.length); // Output: 2
console.log(clonedProfile.achievements.length); // Output: 3

Bibliothèques pour la copie en profondeur

Il existe également une variété de bibliothèques tierces qui offrent une solution de copie en profondeur.

Une fonction de copie profonde de Vanilla JS

Si, pour une raison quelconque, vous ne souhaitez pas utiliser l’objet JSON ou une bibliothèque tierce, vous pouvez également créer une fonction de copie approfondie personnalisée en JavaScript vanille. qui parcourt de manière récursive les propriétés de l’objet et crée un nouvel objet avec les mêmes propriétés et valeurs.

const deepCopy = (obj) => {
    if (typeof obj !== 'object' || obj === null) {
        return obj;
    }

    const newObj = Array.isArray(obj) ? [] : {};

    for (const key in obj) {
        newObj[key] = deepCopy(obj[key]);
    }

    return newObj;
}

const deepCopiedObject = deepCopy(originalObject);

Inconvénients de la copie en profondeur

Bien que la copie en profondeur offre de grands avantages pour la précision des données, il est recommandé d’évaluer si la copie en profondeur est nécessaire pour chaque cas d’utilisation spécifique. Dans certaines situations, la copie superficielle ou d’autres techniques de gestion des références d’objets peuvent être plus appropriées, offrant de meilleures performances et une complexité réduite.

  1. Impact sur les performances : La copie en profondeur peut être coûteuse en calcul, en particulier lorsqu’il s’agit d’objets volumineux ou complexes. Comme le processus de copie approfondie parcourt toutes les propriétés imbriquées, cela peut prendre beaucoup de temps, ce qui a un impact négatif sur les performances de votre application.
  2. Consommation mémoire : La création d’une copie complète entraîne la duplication de toute la hiérarchie d’objets, y compris tous les objets imbriqués. Cela peut entraîner une utilisation accrue de la mémoire, ce qui peut être problématique, en particulier dans les environnements à mémoire limitée ou lorsqu’il s’agit de grands ensembles de données.
  3. Références circulaires : La copie en profondeur peut causer des problèmes lorsque les objets contiennent des références circulaires (c’est-à-dire lorsqu’un objet a une propriété qui se réfère à lui-même, directement ou indirectement). Les références circulaires peuvent entraîner des boucles infinies ou des erreurs de débordement de pile pendant le processus de copie approfondie, et leur gestion nécessite une logique supplémentaire pour éviter ces problèmes.
  4. Gestion des fonctions et des objets spéciaux : La copie en profondeur peut ne pas gérer les fonctions ou les objets avec des caractéristiques spéciales (par exemple, Date, RegExp, éléments DOM) comme prévu. Par exemple, lors de la copie en profondeur d’un objet contenant une fonction, la référence de la fonction peut être copiée, mais la fermeture de la fonction et son contexte lié ne seront pas dupliqués. De même, les objets dotés de caractéristiques spéciales peuvent perdre leurs propriétés et leur comportement uniques lorsqu’ils sont copiés en profondeur.
  5. Complexité de mise en œuvre : L’écriture d’une fonction de copie profonde personnalisée peut être complexe et des méthodes intégrées telles que JSON.parse(JSON.stringify(obj)) ont des limitations, comme ne pas gérer correctement les fonctions, les références circulaires ou les objets spéciaux. Bien qu’il existe des bibliothèques tierces comme celle de Lodash _.cloneDeep() qui peuvent gérer la copie en profondeur plus efficacement, l’ajout d’une dépendance externe pour la copie en profondeur n’est pas toujours idéal.

Conclusion

Merci d’avoir pris le temps de lire cet article. La copie superficielle ou profonde est étonnamment plus complexe que n’importe quel débutant ne l’imagine. Bien qu’il existe de nombreux pièges dans chaque approche, prendre le temps d’examiner et d’examiner les options garantira que votre application et vos données restent exactement comme vous le souhaitez.






Source link