Types et structures de données JavaScript
Les langages de programmation disposent de structures de données natives. Selon les langages, les structures mises à disposition peuvent être différentes. Dans cet article, on listera les structures de données natives en JavaScript. On détaillera leurs propriétés et les façons de les utiliser voire de les combiner. Dans certains cas, on comparera ces structures avec celles d'autres langages.
Un typage dynamique
JavaScript est un langage dont le typage estfaible etdynamique. Cela signifie qu'il n'est pas nécessaire de déclarer le type d'une variable avant de l'utiliser. Le type de la variable sera automatiquement déterminé lorsque le programme sera exécuté. Cela signifie également que la même variable pourra avoir différents types au cours de son existence :
let toto = 42; // toto est un nombretoto = "truc"; // toto est désormais une chaîne de caractèrestoto = true; // toto est désormais un booléen
Les types de données JavaScript
L'ensemble des types disponible en JavaScript se composedes valeurs primitives etdes objets.
Les valeurs primitives (des données immuables, représentées au niveau le plus bas du langage)
Les objets (des ensembles de propriétés)
Les valeurs primitives
Tous les types, sauf les objets, définissent des valeurs immuables (qu'on ne peut modifier). Ainsi, contrairement au C, les chaînes de caractères sont immuables en JavaScript. Les valeurs immuables pour chacun de ces types sont appelées « valeurs primitives ».
Le type booléen
Un booléen représente le résultat d'une assertion logique et peut avoir deux valeurs :true
(pour le vrai logique) etfalse
(pour le faux logique) (voirBoolean
pour plus de détails sur la représentation objet de ce type).
Le type nul
Le type nul ne possède qu'une valeur :null
. Voirnull
etla page du glossaire pour plus d'informations.
Le type indéfini
Une variable à laquelle on n'a pas affecté de valeur vaudraundefined
. Voirundefined
etla page du glossaire pour plus d'informations.
Les types numériques
Le type nombre
Le typeNumber
est géré pour représenter les nombres :les nombres flottants à précision double, représentés sur 64 bits, selon le format IEEE 754. Cette représentation permet de stocker des nombres décimaux entre2^-1074
et2^1024
, mais ne permet de représenter des entiers de façon sûre qu'au sein de l'intervalle allant de-(2^53 − 1)
à2^53 − 1
. Les valeurs en dehors de l'intervalle compris entreNumber.MIN_VALUE
etNumber.MAX_VALUE
sont automatiquement converties en+Infinity
ou-Infinity
, qui se comporteront de façon analogue à l'infini mathématique (voir la page surNumber.POSITIVE_INFINITY
pour les détails et les quelques différences).
Note :Vous pouvez vérifier si un nombre est un nombre entier représentable de façon exacte avec une représentation en nombre flottant à double précision avec la méthodeNumber.isSafeInteger()
. En dehors de l'intervalle entreNumber.MIN_SAFE_INTEGER
etNumber.MAX_SAFE_INTEGER
, JavaScript ne peut plus représenter un entier de façon exacte et ce sera une approximation avec un nombre flottant à double précision.
Pour le typeNumber
, il n'y a qu'un seul nombre qui possède plusieurs représentations :0
qui est représenté comme-0
et+0
(avec0
étant un synonyme pour+0
). En pratique, il n'y a presque pas de différences entre ces représentations et+0 === -0
vauttrue
. Toutefois, on pourra remarquer la nuance lors de la division par zéro :
> 42 / +0Infinity> 42 / -0-Infinity
Dans la plupart des cas, un nombre représente sa propre valeur et JavaScript fournit desopérateurs binaires.
Note :Bien que les opérateurs binairespuissent être utilisés afin de représenter plusieurs valeurs booléennes avec un seul nombre en utilisantun masque de bits, c'est généralement une mauvaise pratique. En effet, JavaScript fournit d'autres moyens pour représenter un ensemble de valeurs booléennes comme les tableaux ou l'utilisation de propriétés nommées pour stocker ces valeurs. L'utilisation d'un masque de bit dégrade également la lisibilité, la clarté et la maintenabilité du code.
Il peut être nécessaire d'utiliser de telles techniques dans des environnements extrêmement contraints, pour gérer des limites de stockage local ou lorsque chaque bit transmis sur le réseau compte. Cette technique devrait uniquement être considérée comme dernière mesure pour réduire la taille.
Le typeBigInt
Le typeBigInt
est un type numérique qui permet de représenter des entiers avec une précision arbitraire. Avec ce type, on peut donc manipuler des entiers plus grands que ceux représentables avecNumber
.
Pour créer un grand entier, on ajoutera unn
après l'entier ou on appellera le constructeurBigInt
.
On peut connaître la valeur la plus grande qui peut être incrémentée et représentée avec le typeNumber
en utilisant la constanteNumber.MAX_SAFE_INTEGER
. Avec les grands entiers, on peut manipuler des nombres qui vont au-delà deNumber.MAX_SAFE_INTEGER
.
Dans l'exemple qui suit, on voit le résultat obtenu lorsqu'on incrémente la valeur deNumber.MAX_SAFE_INTEGER
:
// BigInt> const x = BigInt(Number.MAX_SAFE_INTEGER);9007199254740991n> x + 1n === x + 2n; // 9007199254740992n === 9007199254740993nfalse// Number> Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2; // 9007199254740992 === 9007199254740992true
À l'instar des nombres classiques, on peut utiliser les opérateurs+
,*
,-
,**
et%
. Un grand entier ne sera pas strictement égal à un nombre mais on pourra avoir une égalité faible.
Un grand entier se comportera comme un nombre lorsqu'il est converti en booléen avecif
,||
,&&
,Boolean
et!
.
Il n'est pas possible d'utiliser des grands entiers et des nombres de façon interchangeable. Une exceptionTypeError
sera déclenchée en cas d'incompatibilité.
NaN
NaN
(pourNot A Number en anglais, qui signifie « qui n'est pas un nombre ») est utilisée lorsque le résultat d'une opération arithmétique ne peut pas être exprimée comme un nombre. Il s'agit également de la seule valeur JavaScript qui n'est pas égale à elle-même (du fait de la norme IEEE 754).
Le type chaîne de caractères (String
)
Ce type JavaScript est utilisé afin de représenter des données de texte. C'est un ensemble d'« éléments » de valeurs entières non-signées représentées sur 16 bits. Chaque élément occupe une position au sein de cette chaîne de caractères. Le premier élément est situé à l'indice0
, le deuxième à l'indice1
et ainsi de suite. La longueur d'une chaîne de caractères correspond au nombre d'éléments qu'elle contient.
À la différence d'autres langages (comme le C), les chaînes de caractères JavaScript sont immuables. Cela signifie qu'une fois une chaîne créée, il est impossible de la modifier.
En revanche, il est toujours possible de créer une autre chaîne basée sur la première grâce à des opérations. Par exemple :
- Un fragment de la chaîne originelle en sélectionnant certaines lettres ou en utilisant
String.substr()
. - Une concaténation de deux chaînes de caractères en utilisant l'opérateur de concaténation (
+
) ouString.concat()
.
Attention à ne pas utiliser les chaînes pour tout et n'importe quoi !
Ça peut être tentant de vouloir utiliser des chaînes afin de représenter des données complexes. En revanche, les avantages de cette méthode ne sont que très superficiels :
- On peut facilement construire des chaînes complexes grâce à la concaténation.
- On peut déboguer rapidement le contenu des chaînes de caractères.
- Les chaînes de caractères sont utilisées à de multiples endroits dans beaucoup d'API (champs de saisie, valeurs enstockage local, réponses
XMLHttpRequest
avecresponseText
, etc.).
En utilisant des conventions, il peut être possible de représenter n'importe quelle donnée sous forme d'une chaîne de caractères, en revanche cela n'est souvent pas la meilleure façon. Par exemple, avec un séparateur, on pourrait émuler le comportement d'un tableau en « interdisant » que ce séparateur soit utilisé pour éléments, etc. On pourrait ensuite définir un caractère d'échappement, qui serait à son tour inutilisable dans les chaînes : toutes ces pseudo-conventions entraîneront de lourdes conséquences en termes de maintenance.
En résumé, les chaînes doivent être utilisées pour les données de texte. Pour des données plus complexes, utilisez une abstraction adéquate et analysez/parsez les chaînes que vous recevez d'autres API.
Le type symbole
Un symbole est une valeur primitiveunique etimmuable pouvant être utilisée comme clé pour propriété d'un objet (voir ci-après). Dans d'autres langages de programmation, les symboles sont appelés atomes.
Pour plus de détails, voir les pagesdu glossaire et deSymbol
JavaScript.
Les objets
En informatique, un objet est une valeur conservée en mémoire à laquelle on fait référence grâce à unidentifiant.
Propriétés
En JavaScript, les objets peuvent être considérés comme des collections de propriétés. En utilisantun littéral objet, il est possible d'initialiser un ensemble limité de propriétés ; d'autres propriétés peuvent ensuite être ajoutées et/ou retirées. Les valeurs des propriétés peuvent être de n'importe quel type, y compris des objets. Cela permet de construire des structures de données complexes. Les propriétés sont identifiées grâce à une « clé ». Une clé peut être une chaîne de caractères ou un symbole.
Il existe deux types de propriétés qui ont certains attributs : despropriétés dedonnées (data property) et despropriétés d'accesseur.
Note :Chaque propriété est décrite par desattributs correspondants. Ceux-ci sont utilisés par le moteur JavaScript et ne peuvent pas être manipulés depuis le code. Pour les identifier, les attributs sont indiqués entre double crochets.
Voir la pageObject.defineProperty()
pour en savoir plus.
Propriétés de données
Elles associent une clé avec une valeur et possèdent les attributs suivants :
Attribut | Type | Description | Valeur par défaut |
---|---|---|---|
[[Value]] | N'importe quel type JavaScript | La valeur obtenue lorsqu'on accède à la propriété. | undefined |
[[Writable]] | Booléen | Si cet attribut vautfalse , l'attribut[[Value]] de la propriété ne pourra pas être changé. | false |
[[Enumerable]] | Booléen | Si cet attribut vaut | false |
[[Configurable]] | Booléen | Si cet attribut vautfalse , la propriété ne peut pas être supprimée, ne peut pas être changée en propriété d'accesseur et les attributs en dehors de[[Value]] et[[Writable]] ne pourront pas être changés. | false |
Attribut | Type | Description |
---|---|---|
Read-only | Booléen | État symétrique pour l'attribut ES5[[Writable]] . |
DontEnum | Booléen | État symétrique pour l'attribut ES5[[Enumerable]] . |
DontDelete | Booléen | État symétrique pour l'attribut ES5[[Configurable]] . |
Propriétés d'accesseur
Ces propriétés associent une clé avec une ou deux fonctions accesseur et mutateur (respectivementget
etset
) qui permettent de récupérer ou d'enregistrer une valeur.
Note :Il est important de noter qu'on parle depropriété d'accesseur et pas deméthode. On peut donner des accesseurs semblables à ceux d'une classe à un objet en utilisant une fonction comme valeur d'une propriété mais ça ne fait pas de l'objet une classe.
Elles possèdent les attributs suivants :
Attribut | Type | Description | Valeur par défaut |
---|---|---|---|
[[Get]] | Un objetFunction ouundefined | La fonction qui est appelée sans argument afin de récupérer la valeur de la propriété quand on souhaite y accéder. Voir aussi la page surget . | undefined |
[[Set]] | Un objetFunction ouundefined | La fonction, appelée avec un argument qui contient la valeur qu'on souhaite affecter à la valeur et qui est exécutée à chaque fois qu'on souhaite modifier la valeur. Voir aussi la page surset . | undefined |
[[Enumerable]] | Booléen | S'il vauttrue , la propriété sera listée dans les bouclesfor…in . | false |
[[Configurable]] | Booléen | S'il vautfalse , la propriété ne pourra pas être supprimée et ne pourra pas être transformée en une propriété de données. | false |
Les objets « normaux » et les fonctions
Un objet JavaScript est un ensemble de correspondances entre desclés et desvaleurs. Les clés sont représentées par des chaînes ou des symboles (Symbol
). Les valeurs peuvent être de n'importe quel type. Grâce à cela, les objets peuvent, naturellement, être utilisés commetables de hachage.
Les fonctions sont des objets classiques à la seule différence qu'on peut les appeler.
Les dates
Lorsqu'on souhaite représenter des dates, il est tout indiqué d'utiliser le type utilitaire natifDate
de JavaScript.
Les collections indexées : les tableaux (Arrays) et les tableaux typés (Typed Arrays)
Les tableaux (ouArrays en anglais) sont des objets natifs qui permettent d'organiser des valeurs numérotées et qui ont une relation particulière avec la propriétélength
.
De plus, les tableaux héritent deArray.prototype
qui permet de bénéficier de plusieurs méthodes pour manipuler les tableaux. Par exemple,indexOf()
qui permet de rechercher une valeur dans le tableau oupush()
qui permet d'ajouter un élément au tableau. Les tableaux sont donc indiqués quand on souhaite représenter des listes de valeurs ou d'objets.
Les tableaux typés (Typed Arrays en anglais) ont été ajoutés avec ECMAScript 2015 et offrent une vue sous forme d'un tableau pour manipuler des tampons de données binaires. Le tableau qui suit illustre les types de données équivalents en C :
Type | Intervalle | Taille (exprimée en octets) | Description | Type Web IDL | Type équivalent en C |
Int8Array | -128 à 127 | 1 | Entier signé en complément à deux sur 8 bits. | byte | int8_t |
Uint8Array | 0 à 255 | 1 | Entier non signé sur 8 bits. | octet | uint8_t |
Uint8ClampedArray | 0 à 255 | 1 | Entier non signé sur 8 bits (compris entre 0 et 255). | octet | uint8_t |
Int16Array | -32768 à 32767 | 2 | Entier signé en complément à deux sur 16 bits. | short | int16_t |
Uint16Array | 0 à 65535 | 2 | Entier non signé sur 16 bits. | unsigned short | uint16_t |
Int32Array | -2147483648 à 2147483647 | 4 | Entier signé en complément à deux sur 32 bits. | long | int32_t |
Uint32Array | 0 à 4294967295 | 4 | Entier non signé sur 32 bits. | unsigned long | uint32_t |
Float32Array | 1.2x10^-38 à 3.4x10^38 | 4 | Nombre flottant sur 32 bits selon la représentation IEEE (7 chiffres significatifs). | unrestricted float | float |
Float64Array | 5.0x10^-324 à 1.8x10^308 | 8 | Nombre flottant sur 64 bits selon la représentation IEEE (16 chiffres significatifs). | unrestricted double | double |
BigInt64Array | -2^63 à 2^63-1 | 8 | Nombre entier signé sur 64 bits en complément à deux. | bigint | int64_t (signed long long) |
BigUint64Array | 0 à 2^64-1 | 8 | Nombre entier non signé sur 64 bits. | bigint | uint64_t (unsigned long long) |
Les collections avec clés :Map
,Set
,WeakMap
,WeakSet
Ces structures de données utilisent des clés pour référencer des objets. Elles ont été introduites avec ECMAScript 2015.Set
etWeakSet
représentent des ensembles d'objets,Map
etWeakMap
associent une valeur à un objet.
Il est possible d'énumérer les valeurs contenues dans un objetMap
mais pas dans un objetWeakMap
. LesWeakMap
quant à eux permettent certaines optimisations dans la gestion de la mémoire et le travail du ramasse-miettes.
Il est possible d'implémenter des objetsMap
etSet
grâce à ECMAScript 5. Cependant, comme les objets ne peuvent pas être comparés (avec une relation d'ordre par exemple), la complexité obtenue pour rechercher un élément serait nécessairement linéaire. Les implémentations natives (y compris celle desWeakMap
) permettent d'obtenir des performances logarithmiques voire constantes.
Généralement, si on voulait lier des données à un nœud DOM, on pouvait utiliser les attributsdata-*
ou définir les propriétés à un même l'objet. Malheureusement, cela rendait les données disponibles à n'importe quel script fonctionnant dans le même contexte. Les objetsMap
etWeakMap
permettent de gérer plus simplement une liaison « privée » entre des données et un objet.
Les données structurées : JSON
JSON (JavaScript Object Notation) est un format d'échange de données léger, dérivé de JavaScript et utilisé par plusieurs langages de programmation. JSON permet ainsi de construire des structures de données universelles pouvant être échangées entre programmes.
Pour plus d'informations, voirla page du glossaire etla page surJSON
.
Les autres objets de la bibliothèque standard
JavaScript possède une bibliothèque standard d'objets natifs. Veuillez lire laréférence pour en savoir plus sur ces objets.
Déterminer le type des objets grâce à l'opérateurtypeof
L'opérateurtypeof
peut vous aider à déterminer le type d'une variable. Pour plus d'informations et sur les cas particuliers, voir la page de référence surcet opérateur.
Voir aussi
- Structures de données et algorithmes JavaScript par Oleksii Trekhleb (en anglais)
- Un ensemble de structures de données usuelles et d'algorithmes classiques, en JavaScript, par Nicholas Zakas (en anglais)
- Implémentations de différentes structures de données et utilitaires de recherche en JavaScript (en anglais)
- Types de données et valeurs dans la spécification ECMAScript (en anglais)