TXRIGXN
  • Accueil
  • A propos
  • Blog
  • Projets

Shallow vs Deep Copy en JavaScript

18 août 2026 - temps de lecture: 15 min
JavaScript Apprentissage Informatique

Shallow vs Deep Copy en Javascript

On utilise chaque jour en JavaScript ou avec les frameworks frontend des méthodes comme map(), slice() ou encore filter() sans réllement savoir ce qui se passe derrière. Parfois, on remarque des comportements surprenant et on se dit “c’est quoi ce langage…”.

Par exemple :

const user = {
  name: "alice",
  preferences: {
    theme: "dark"
  }
};

const copy = { ...user };

copy.preferences = "light";

console.log(user.preferences.theme); // Output: 'light'

Mais pourquoi user a-t-il changé alors que j’ai modifié copy ?

Tout d’abord, c’est quoi « copier » en informatique ?

En général, quand on pense au fait de copier, on se dit que l’orignal ne se modifie pas, si on fait une modification sur sa copie.

En informatique, c’est un peu plus subtil que ca.

Quand on travaille avec des objets en JavaScript, deux variables peuvent faire références au même objet.

Par exemple :

const user = {
  name: "Alice"
};

const copy = user;

copy.name = "foo";

console.log(user.name); // Output: foo
console.log(copy.name); // Output: foo

console.log(user === copy); // Output: true

Dans ce cas, modifier l’objet à travers une variable modifiera également la première variable, car elles ont la même référence en mémoire. Ce n’est pas une copie à proprement parler, ce sont juste deux variables qui font référence au même objet.

Valeurs primitives et objets

Pour comprendre les copies en JavaScript, il est important de distinguer les valeurs primitives des objets.

Il y a 7 types de valeurs primitives en JavaScript :

  • String

  • Number

  • Boolean

  • Symbol

  • Undefined

  • Null

  • BigInt

Avec les objets, ce sont les variables qui permettent d’accéder à un même objet via une référence.

C’est cette notion de référence qui est importante pour comprendre les shallow copies.

A propos de la Stack et du Heap

J’avais fait toute une partie sur la gestion en mémoire en JavaScript en disant que « les primitives sont stockées dans la Stack et les objets dans la Heap » mais c’est une simplification qui n’est pas très juste et ce n’est pas défini de cette manière dans les spécifications JavaScript.

Au final, on a pas besoin d’entrer dans ce niveau de détail d’implémentation pour comprendre les shallow et deep copies.


Shallow vs Deep Copy en pratique

Une fois que l’on comprend la différence entre une valeur primitive et une référence vers un objet, on peut définir les shallow et deep copies.

On parle également de « copie superficielle » et de « copie profonde ».

Shallow copy

Une shallow copy crée un nouvel objet et copie ses propriétés de premier niveau.

const lang = {
  fr: "Bonjour",
  de: "Hallo",
  en: "Hello"
};

const foo = { ...lang };

console.log(foo === lang); // output: false

foo.fr = "Salut";

console.log(foo.fr); // output: Salut
console.log(lang.fr); // output: Bonjour

C’est ce qu’on appelle une Shallow Copy, mais ca peut être problématique quand on s’attend que l’objet original garde ses valeurs de base. Cette facon de faire est souvent proie à des erreurs et bugs « silencieux ».

En revanche, si une propriété contient elle-même un objet, cet objet n’est pas copié. La copie et l’original continuent alors de faire référence au même objet imbriqué.

Mais que se passe-t-il lorsqu’une propriété contient elle-même un objet ?

const lang = {
  config: {
    fr: "bonjour"
  }
};

const foo = { ...lang };

console.log(foo === lang); // output : false donc 2 objets différents
console.log(foo.config === lang.config); // output: true donc réf au même objet

Ici, foo et lang sont bien deux objets différents. En revanche, foo.config et lang.config font référence au même objet.

Si l’on modifie l’objet imbriqué à travers foo:

foo.config.fr = "salut";

console.log(foo.config.fr); // output: 'salut'
console.log(lang.config.fr); // output: 'salut'

La modification est également visible dans lang. C’est caractéristique d’une shallow copy : l’objet de premier niveau est copié, mais les objets imbriqués ne le sont pas.

Les 2 façons de shallow copy

1. Utiliser le Spread Operator (ES6/ES2015)

C’est le moyen le plus courant aujourd’hui pour créer une shallow copy d’un objet est d’utiliser le spread operator :

const lang = {
  fr: "Bonjour",
  de: "Hallo",
  en: "Hello"
};

const foo = { ...lang };
foo.fr = "Salut";

console.log(foo.fr); // output: Salut
console.log(lang.fr); // output: Bonjour
2. Object.assign()

C’est l’ancienne façon de faire avant ES6

const lang = {
  fr: "Bonjour",
  de: "Hallo",
  en: "Hello"
};

const foo = Object.assign({}, lang);
foo.fr = "Salut";

console.log(foo.fr); // output: Salut
console.log(lang.fr); // output: Bonjour

Mais on va avoir un problème ici, ce sont les objets imbriqués. En effet, si on change une donnée dans l’objet imbriqué, cela va aussi changer les données originales.

const lang = {
  config: {
    fr: "bonjour"
  }
};
const foo = { ...lang };

lang.config.fr = "salut";

console.log(lang.config.fr); // output: salut
console.log(foo.config.fr); // output: salut

Ici, foo et lang sont deux objets différents, mais ils partagent le même objet config:

console.log(foo === lang); // output: false
console.log(foo.config === lang.config); // output: true

C’est donc bien une Shallow Copy.

Maintenant, si on veut que l’objet copié soit complètement indépendant de l’original, y compris pour ses objets imbriqués. On va alors avoir besoin de faire une Deep Copy.

Deep Copy

Par ailleurs, une « copie profonde » ou deep copy est un objet dans lesquels ces propriétés ne partagent plus du tout de références avec son objet d’origine.

On retrouve une immutabilité des données, c’est-à-dire qu’on ne pourra plus modifier l’objet source si on modifie notre copie.

On peut reprendre notre exemple précédent, si on avait voulu faire une « copie profonde », on peut utiliser structuredClone() qui est une version plus moderne et plus fiable que la méthode avec JSON.parse(JSON.stringify(object)) où on pouvait perdre des éléments de notre objet et certains types n’était pas pris en compte. On peut citer quelques exemples:

  • Les fonctions avec closures

  • Symbols

  • Les objets représentants des éléments HTML

  • Données recursives

  • etc.

C’est disponible sur tous les navigateurs modernes depuis 2022 et ça été introduit avec la version 17 de Node.js.

const lang = {
  config: {
    fr: "bonjour"
  }
};
const foo = structuredClone(lang);

console.log(foo === lang); // output: false
console.log(lang.config === foo.config); // output: false

foo.config.fr = "salut";

console.log(lang.config.fr); // output: 'bonjour'
console.log(foo.config.fr); // output: 'salut'

Shallow, deep copy et immutabilité

J’ai parlé en introduction de méthodes de tableaux comme map(), slice() ou filter(), je pensais que ces méthodes renvoyaient une copie mais pas du tout… Bah non, ce sont juste des fonctions pour manipuler des tableaux, tout simplement. Cela ne veut pas dire que ces méthodes vont réaliser une copie profonde.

Mais du coup, c’était important pour moi de faire cette distinction entre la shallow, deep copy lorsqu’on cherche à manipuler des données immutables.

Une shallow copy permet d’éviter de modifier directement l’objet original, mais les objets imbriqués peuvent toujours partager leurs références.

Une deep copy permet, elle, de rendre toute la structure indépendante.

Au final, ma compréhension de cette différence va surtout me permettre de mieux savoir ce que je manipule et d’éviter des modifications non voulues de mes données de base.

Références

MDN: Deep Copy

MDN: Shallow Copy

Article précédent icon
Article Précédent

Introduction aux Git Submodules

codeberg Icon linkedin Icon mail Icon rss Icon

©2026 - Réalisé par TXRIGXN

Inspiré par Multiterm & infinull

Favicon et bannière héro réalisés par eilosyne

  • Accueil icon Accueil
  • Blog icon Blog
  • Codeberg icon Projets
  • A propos page icon A propos