Le deep-copying en JavaScript avec structuredClone()

Pour réaliser un deep clone d'un objet en Node.js, plus besoin de bibliothèque externe ou de passer par la méthode JSON.parse. Vous pouvez désormais utiliser la nouvelle fonction globale structuredClone(). Explications 👇

Shallow copy

La copie d'un objet en JavaScript est presque toujours shallow (c'est-à-dire une copie par référence : l'objet source et la copie référencent tous deux le même objet), par opposition à deep (c'est-à-dire un clone réel et indépendant). Les modifications effectuées aux propriétés imbriquées de l'objet se répercuteront dans l'objet original et dans la copie de l'objet.

Avec l'opérateur de propagation : ...

Une shallow copy peut être créée avec l'opérateur de propagation «...» :

    
const myOriginal = {
    aubergine: '🍆',
    apple: '🍏',
    recipes: {
        pie: '🥧',
        hamburger: '🍔'
    }
};
const myShallowCopy = {...myOriginal};
    

Ajouter ou modifier une propriété directe dans la copie n'affectera pas l'original...

    
myShallowCopy.mushroom = '🍄';
console.log(myShallowCopy.mushroom) // '🍄'
console.log(myOriginal.mushroom) // undefined
    

En revanche, ajouter ou modifier une propriété imbriquée (deeply nested) affectera et l'objet d'origine et la copie de l'objet :

    
myShallowCopy.recipes.sushi = '🍣';
console.log(myShallowCopy.recipes.sushi) // '🍣'
console.log(myOriginal.recipes.sushi) // '🍣'
    

L'expression {...myOriginal} itère sur les différentes propriétés de l'objet myOriginal grâce à l'opérateur de propagation. Il récupère le nom et la valeur des propriétés et les assigne une par une à un nouvel objet vide. Le nouvel objet qui en résulte est donc identique sur la forme, mais avec sa propre copie de la liste des propriétés et de leurs valeurs. Les valeurs sont également copiées, mais les valeurs dites primitives sont gérées différemment par JavaScript des valeurs non-primitives.

Selon MDN : « Une primitive (valeur primitive ou structure de donnée primitive) est une donnée qui n'est pas un objet et n'a pas de méthode. En JavaScript, il y a 6 types de données primitives: String, Number, Boolean, Null, undefined, Symbol.»

Les valeurs non-primitives (comme les objets imbriqués) sont gérées par référence.

Avec la méthode Object.assign()

Une autre façon de créer une shallow copy est d'utiliser la méthode Object.assign() :

    
const myOriginal = {
    aubergine: '🍆',
    apple: '🍏'
};
const myShallowCopy = Object.assign({}, myOriginal);
    

Deep copy

L'opposé d'une shallow copy est une deep copy. Un algorithme de clone réel (deep copy) copie également les propriétés d'un objet une à une, mais s'invoque de manière récursive quand il rencontre une référence à un autre objet imbriqué, créant ainsi un clone de cet objet-là également.

Pendant longtemps, il n'y a pas eu de méthode évidente pour créer un clone réel d'un objet en JavaScript. La plupart des développeurs avaient recours à des bibliothèques tierces comme Lodash et sa fonction cloneDeep(). L'une des méthodes les plus usitées consistait à utiliser du JSON :

    
const myDeepCopy = JSON.parse(JSON.stringify(myOriginal)); // 😭💀
    

Aujourd'hui, les choses ont enfin changé ! Les développeurs ont désormais accès à la fonction structuredClone() :

    
const myDeepCopy = structuredClone(myOriginal); // 👍
    

Hooray ! 🎉 Malheureusement, cette fonction contient tout de même quelques limitations. Si l'objet à copier contient des fonctions, par exemple, elles seront simplement ignorées.

Nicolas Lépinay

Étudiant en informatique, féru d'UI/UX design et curieux de tout.