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 est faible et dynamique. 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 :
var toto = 42; // toto est un nombre var toto = "machin"; // toto est une chaîne de caractères désormais var toto = true; // et maintenant, toto est un booléen
Les types de données
Le dernier standard ECMAScript définit 7 types de données :
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) et false
(pour le faux logique)
Le type nul
Le type nul ne possède qu'une valeur : null
. Voir null
et Null pour plus d'informations.
Le type indéfini
Une variable à laquelle on n'a pas affecté de valeur vaudra undefined
. Voir undefined
et Undefined pour plus d'informations.
Le type nombre
Selon le standard ECMAScript, un seul type 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 (les nombres compris entre -(253 -1) et 253 -1). Il n'y a donc pas de type à part pour représenter les nombres entiers. En plus de sa capacité à représenter les nombres décimaux, le type nombre possède trois valeurs symboliques : +Infinity
, -Infinity
, et NaN
(Not A Number en anglais, qui signifie « n'est pas un nombre »).
Afin de vérifier que des valeurs sont supérieures/inférieures à +/-Infinity
, on peut utiliser les constantes Number.MAX_VALUE
et Number.MIN_VALUE
. À partir d'ECMAScript 6, on peut également vérifier si un nombre est/sera compris dans l'intervalle de représentation pour les nombres flottants à précision double en utilisant la méthode Number.isSafeInteger()
ainsi que les valeurs Number.MAX_SAFE_INTEGER
et Number.MIN_SAFE_INTEGER
. En dehors de cet intervalle et pour JavaScript, on considère que les nombres ne sont plus représentés correctement.
Le type nombre possède un seul entier pouvant être représenté de deux façons différentes : 0 qui peut être représenté par -0 et +0. ("0" étant un alias pour +0). En pratique, cela n'a généralement aucun impact et +0 === -0
vaut bien true
. Malgré tout, on peut observer certaines différences quand on divise par zéro :
42 / +0 // Infinity 42 / -0 // -Infinity
Dans la plupart des cas, un nombre représente sa propre valeur, malgré tout les opérateurs binaires peuvent être utilisés pour représenter plusieurs valeurs booléennes grâce à un seul nombre (on parle alors de masque de bits). Ceci est généralement une mauvaise pratique (lisibilité, maintenabilité) bien que ça puisse être utile lorsqu'on souhaite minimiser le nombre de bits qu'on utilise.
Le type chaîne de caractères (String)
Ce type JavaScript est utilisé afin de représenter des données textuelles. 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'indice 0, le deuxième à l'indice 1 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 des chaînes de caractères dans le langage C, les chaînes de caractères JavaScript sont immuables. Cela signifie qu'une fois qu'une chaîne est 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 en stockage 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 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 textuelles. 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
Les symboles sont une nouveautés du langage, apportée par ECMAScript 6. Un symbole est une valeur primitive unique et immuable 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. On peut également dresser un parallèle entre eux et les énumérations (enum) en C. Pour plus de détails, voir les pages Symbole et le constructeur Symbol
JavaScript.
Les objets
En informatique, un objet est une valeur conservée en mémoire à laquelle on fait référence grâce à un identifiant.
Propriétés
En JavaScript, les objets peuvent être considérés comme des collections de propriétés. En utilisant un 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 : des propriétés de données (data property) et des propriétés d'accesseur.
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 quelle valeur JavaScript | La valeur obtenue lorsqu'on accède à la propriété. | undefined |
[[Writable]] | Booléen | S'il vaut false , la valeur de la propriété (l'attribut [[Value]]) ne peut être changé. |
false |
[[Enumerable]] | Booléen | S'il vaut true , la propriété pourra être listée par une boucle for...in . Voir également l'article sur le caractère énumérable des propriétés. |
false |
[[Configurable]] | Booléen | S'il vaut false , la propriété ne pourra pas être supprimée et les attributs autres que [[Value]] et [[Writable]] ne pourront pas être modifié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 un ou deux fonctions accesseur et mutateur qui permettent de récupérer ou d'enregistrer une valeur. Elles possèdent les attributs suivants :
Attribut | Type | Description | Valeur par défaut |
---|---|---|---|
[[Get]] | Un objet Function ou undefined |
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 sur get . |
undefined |
[[Set]] | Un objet Function ou undefined |
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 sur set . |
undefined |
[[Enumerable]] | Booléen | S'il vaut true , la propriété sera listée dans les boucles for...in . |
false |
[[Configurable]] | Booléen | S'il vaut false , la propriété ne pourra pas être supprimée et ne pourra pas être transformée en une propriété de données. |
false |
Note : Les attributs sont généralement utilisés par le moteur JavaScript plutôt qu'explicitement dans les scripts. Il est impossible d'y accéder directement (plus d'informations sur Object.defineProperty()
. C'est pour cela que l'attribut est décrit entre double crochets (comme dans la spécification ECMAScript) plutôt qu'entre crochets simples qui pourraient laisser penser à une propriété « classique ».
Les objets « normaux » et les fonctions
Un objet JavaScript est un ensemble de correspondances entre des clés et des valeurs. 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 comme tables 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 natif Date
de JavaScript.
Les collections indexées : les tableaux (Arrays) et les tableaux typés (Typed Arrays)
Les tableaux (ou Arrays 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 de Array.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 ou push
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 6 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 :
Les objets TypedArray
Type | Taille (exprimée en octets) | Description | Type Web IDL | Type équivalent en C |
Int8Array |
1 | Entier signé en complément à deux sur 8 bits | byte |
int8_t |
Uint8Array |
1 | Entier non signé sur 8 bits | octet |
uint8_t |
Uint8ClampedArray |
1 | Entier non signé sur 8 bits (compris entre 0 et 255) | octet |
uint8_t |
Int16Array |
2 | Entier signé en complément à deux sur 16 bits | short |
int16_t |
Uint16Array |
2 | Entier non signé sur 16 bits | unsigned short |
uint16_t |
Int32Array |
4 | Entier signé en complément à deux sur 32 bits | long |
int32_t |
Uint32Array |
4 | Entier non signé sur 32 bits | unsigned long |
uint32_t |
Float32Array |
4 | Nombre flottant sur 32 bits selon la représentation IEEE | unrestricted float |
float |
Float64Array |
8 | Nombre flottant sur 64 bits selon la représentation IEEE | unrestricted double |
double |
Les collections avec clés : Maps, Sets, WeakMaps, WeakSets
Ces structures de données utilisent des clés pour référencer des objets. Elles ont été introduites avec ECMAScript 6. Set
et WeakSet
représentent des ensembles d'objets, Map
et WeakMap
associent une valeur à un objet. Il est possible d'énumérer les valeurs contenues dans un objet Map mais pas dans un objet WeakMap. Les WeakMaps quant à eux permettent certaines optimisations dans la gestion de la mémoire et le travail du ramasse-miettes.
Il est possible d'implémenter les Maps et Sets 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 des WeakMaps) 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 attributs data-*
ou définir les propriétés à même l'objet. Malheureusement, cela rendait les données disponibles à n'importe quel script fonctionnant dans le même contexte. Les Maps et WeakMaps 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, voir les pages JSON et JSON
.
Les autres objets de la bibliothèque standard
JavaScript possède une bibliothèque standard d'objets natifs. Veuillez lire la référence pour en savoir plus sur ces objets.
Déterminer le type des objets grâce à l'opérateur typeof
L'opérateur typeof
peut vous aider à déterminer le type d'une variable. Pour plus d'informations et sur les cas particuliers, voir la page de la référence sur cet opérateur.
Spécifications
Spécification | État | Commentaires |
---|---|---|
ECMAScript 1st Edition (ECMA-262) | Standard | Définition initiale. |
ECMAScript 5.1 (ECMA-262) La définition de 'Types' dans cette spécification. |
Standard | |
ECMAScript 2015 (6th Edition, ECMA-262) La définition de 'ECMAScript Data Types and Values' dans cette spécification. |
Standard | |
ECMAScript 2017 Draft (ECMA-262) La définition de 'ECMAScript Data Types and Values' dans cette spécification. |
Projet |