Comprendre les primitives et les références en Javascript

Il n'y a pas toujours besoin de connaitre le fonctionnement interne du langage, mais certains concepts sont indispensables !

Article publié le 16/02/2021, dernière mise à jour le 19/09/2023

En Javascript, toutes les données ne sont pas gérées de la même manière, ni au même endroit dans la mémoire, ce qui peut porter à confusion et entrainer quelques erreurs, nous allons donc voir ensemble pour comprendre toutes ces subtilités.

Lorsque vous et déclarez et initialisez une nouvelle variable, vous allez réserver un espace dans la mémoire de la machine.

Généralement dans la RAM, la mémoire vive.

Cet espace sera plus ou moins grand selon la donnée que vous allez devoir y stocker (1 bit pour un booléen, 8 bit pour un caractère, etc...), l'objectif étant que votre programme prenne le moins de place possible en mémoire, et surtout ne gaspille pas de la place inutilement.

S'il est possible de connaitre à l'avance la taille d'une donnée pour certains types (comme les booléens), ce n'est pas le cas pour tous, comme les types complexes par exemple (object, array, function, etc...)

Voilà pourquoi Javascript met à disposition deux grands types de données bien différents : les données primitives et les références.

Les types primitifs

Il se comptent au nombre de 7 en Javascript, et sont : string, number, bigint, boolean, undefined, null et symbol.

La caractéristique principale de ces types est que leurs valeurs sont "immutables" (immuables en français), ce qui signifie qu'elles ne peuvent pas changer.

Pourtant lorsque je déclare une nouvelle chaine de caractère, je peux changer sa valeur !

On pourrait le croire, mais en fait non.

Chaque nouvelle donnée est stockée à une adresse mémoire bien précise (là où débute la donnée), et lorsque vous modifiez une primitive, vous ne modifiez pas la valeur à l'emplacement de l'adresse mémoire, mais vous demandez une nouvelle adresse disponible pour y stocker cette nouvelle donnée.

Exemple :


let nbr = 5; //L'adresse (fictive) 0x0001 contient le chiffre 5
nbr = 3; //Une nouvelle adresse 0x0002 contient désormais le chiffre 3

//La case mémoire 0x0001 sera libérée dès que possible, car plus utilisée

Une valeur primitive n'est jamais altérée, la variable qui pointe dessus, elle, peut être réassignée (sauf avec const) mais la valeur ne sera pas modifiée.

Pour vous le prouver, vous pouvez essayer cette petite expérience, directement dans votre console :


let a = "hello"; //Adresse 0x0001
let b = a; //Adresse 0x0001

a += " world" //Adresse 0x0002
console.log(a); // -> "hello world";
console.log(b); // -> "hello";

Vous voyez, même si la variable b pointe sur la même adresse que la variable a au départ, lorsque l'on modifie la valeur de a, la valeur de b ne change pas, car une nouvelle adresse mémoire a été assignée à la variable a.

Les valeurs primitives sont donc stockées dans un espace appelé la mémoire "statique", aussi appelée "stack".

Les références

Contrairement aux valeurs primitives, les valeurs complexes elles sont stockées dans la partie "dynamique" de la mémoire car leur taille est variable et peut évoluer au fil de l'exécution du programme, les rendant donc "imprévisibles".

Imaginez qu'une variable ait besoin de 8 bits de place pour être stockée, si le système d'exploitation trouve un espace de 8 bits libres entre deux autres données, alors il va pouvoir exploiter cet espace et le dédier à cette nouvelle variable.

Mais imaginons maintenant que notre variable n'a plus besoin de 8 bits mais de 16 car elle a grossie, alors notre variable ne va plus loger à l'adresse actuelle et va devoir être déplacée ailleurs dans la mémoire.

Nous avons donc besoin d'une variable qui ne contient non plus une simple valeur, mais qui contient une référence vers une adresse mémoire qui peut changer, cette référence est donc dynamique et doit pointer vers l'objet, quelle que soit son emplacement dans la mémoire.

On parle donc de mémoire dynamique, aussi appelée "heap".

Ce qui induit un comportement différent lors de la modification d'une variable.

On ne modifie plus la valeur derrière la variable, mais on modifie la valeur derrière la référence derrière la variable, donc si plusieurs variables contiennent la même référence, la modification de l'objet sera effective pour toutes les variables qui pointent dessus.

Exemple :


let a = {txt: "hello"}; //Référence r0 vers l'objet
let b = a; //Référence r0 vers le même objet

a.txt += " world" //Adresse 0x0002
console.log(a); //"hello world";
console.log(b); //"hello world";

Nos deux variables ne stockent donc plus des valeurs, mais des références vers une même valeur qui peut changer.


Ksenia Kudelkina sur Unsplash

Vous avez terminé l'article ?

Commentaires (0)

pour laisser un commentaire

Aucun commentaire pour l'instant