15 mai 2024

Arrays

Les objets vous permettent de stocker des collections de valeurs à clé. C’est très bien.

Mais assez souvent, nous trouvons qu’il nous faut une collection ordonnée, où nous avons un 1er, un 2ème, un 3ème élément, etc. Par exemple, nous avons besoin de cela pour stocker une liste de quelque chose : utilisateurs, trucs, éléments HTML, etc.

Il n’est pas pratique d’utiliser un objet ici, car il ne fournit aucune méthode pour gérer l’ordre des éléments. Nous ne pouvons pas insérer une nouvelle propriété “entre” celles existantes. Les objets ne sont tout simplement pas destinés à un tel usage.

Il existe une structure de données spéciale appelée Array (tableau), pour stocker les collections ordonnées.

Déclaration

Il existe deux syntaxes pour créer un tableau vide :

let arr = new Array();
let arr = [];

La plupart du temps c’est la deuxième syntaxe qui est utilisée. Nous pouvons fournir des éléments initiaux entre parenthèses :

let fruits = ["Apple", "Orange", "Plum"];

Les éléments de tableau sont numérotés en commençant par zéro.

On peut obtenir un élément par son numéro grace aux crochets :

let fruits = ["Apple", "Orange", "Plum"];

alert( fruits[0] ); // Apple
alert( fruits[1] ); // Orange
alert( fruits[2] ); // Plum

Nous pouvons remplacer un élément :

fruits[2] = 'Pear'; // maintenant ["Apple", "Orange", "Pear"]

…Ou en ajouter un nouveau au tableau :

fruits[3] = 'Lemon'; // maintenant ["Apple", "Orange", "Pear", "Lemon"]

Le nombre total d’éléments dans le tableau est sa length (longueur) :

let fruits = ["Apple", "Orange", "Plum"];

alert( fruits.length ); // 3

Nous pouvons également utiliser un alert pour afficher l’ensemble du tableau :

let fruits = ["Apple", "Orange", "Plum"];

alert( fruits ); // Apple,Orange,Plum

Un tableau peut stocker des éléments de tout type.

Par exemple :

// mélange de valeurs
let arr = [ 'Apple', { name: 'John' }, true, function() { alert('hello'); } ];

// récupère l'objet à l'index 1 et montre ensuite son nom
alert( arr[1].name ); // John

// affiche la fonction à l'index 3 et l'exécute la
arr[3](); // hello
Trailing comma (virgule de fin)

Un tableau, comme pour un objet, peut se terminer par une virgule :

let fruits = [
  "Apple",
  "Orange",
  "Plum",
];

Le style “virgule de fin” facilite l’insertion et la suppression d’éléments, car toutes les lignes se ressemblent.

Récupérer les derniers éléments avec “at”

Un ajout récent
Ceci est un ajout récent au language. Les anciens navigateurs peuvent nécessiter des polyfills.

Disons que nous voulons le dernier élément du tableau.

Certains langages de programmation permettent l’utilisation d’index négatifs pour ça, comme fruits[-1].

Tandis qu’en JavaScript ça ne fonctionnera pas. Le résultat sera undefined, parce que l’index dans les crochets est traité littéralement.

Nous pouvons calculer explicitement l’index du dernier élément et donc y accéder: fruits[fruits.length - 1].

let fruits = ["Apple", "Orange", "Plum"];

alert( fruits[fruits.length-1] ); // Plum

Un peu lourd, n’est-ce pas ? Nous devons écrire le même nom de variable deux fois.

Heureusement, il y a une syntaxe plus courte : fruits.at(-1) :

let fruits = ["Apple", "Orange", "Plum"];

// Identique à fruits[fruits.length-1]
alert( fruits.at(-1) ); // Plum

En d’autres termes, arr.at(i):

  • est exactement identique à arr[i], si i >= 0.
  • pour les valeurs négatives de i, ça recule depuis la fin du tableau.

Les méthodes pop/push, shift/unshift

Une queue (file d’attente) est l’une des utilisations les plus courantes pour les tableaux. En informatique, cela signifie une collection ordonnée d’éléments qui supporte deux opérations :

  • push ajoute un élément à la fin.
  • shift enlève un élément depuis le début, en faisant avancer la file d’attente, de sorte que le deuxième élément devienne le premier.

Les tableaux prennent en charge les deux opérations.

En pratique, nous en avons besoin très souvent. Par exemple, une file d’attente de messages devant être affichés à l’écran.

Il y a un autre cas d’utilisation pour les tableaux – la structure de données nommée stack.

Il supporte deux opérations :

  • push ajoute un élément à la fin.
  • pop enlève un élément de la fin.

Ainsi, de nouveaux éléments sont ajoutés ou enlevés toujours à partir de la “fin”.

Un stack (pile) est généralement illustrée par un jeu de cartes. De nouvelles cartes sont ajoutées ou enlevées par le haut :

Pour les stacks (piles), le dernier élément envoyé est reçu en premier, c’est le principe LIFO (Last-In-First-Out, dernier entré, premier sorti). Pour les files d’attente, nous avons FIFO (First-In-First-Out, premier entré, premier sorti).

Les tableaux en JavaScript peuvent fonctionner à la fois en queue et en stack. Ils vous permettent d’ajouter ou supprimer des éléments à la fois par le début ou par la fin.

En informatique, la structure de données qui permet cela s’appelle deque.

Méthodes qui fonctionnent avec la fin du tableau :

pop

Extrait le dernier élément du tableau et le renvoie :

let fruits = ["Apple", "Orange", "Pear"];

alert( fruits.pop() ); // supprime "Pear" et l'alerte

alert( fruits ); // Apple, Orange

Les deux méthodes fruits.pop() et fruits.at(-1) renvoient le dernier élément du tableau, mais fruits.pop() modifie également le tableau en supprimant l’élément.

push

Ajoute l’élément à la fin du tableau :

let fruits = ["Apple", "Orange"];

fruits.push("Pear");

alert( fruits ); // Apple, Orange, Pear

L’appel de fruits.push(...) est égal à fruits[fruits.length] = ....

Méthodes qui fonctionnent avec le début du tableau :

shift

Extrait le premier élément du tableau et le renvoie :

let fruits = ["Apple", "Orange", "Pear"];

alert( fruits.shift() ); // supprime "Apple" et l'alerte

alert( fruits ); // Orange, Pear
unshift

Ajoute l’élément au début du tableau :

let fruits = ["Orange", "Pear"];

fruits.unshift("Apple");

alert( fruits ); // Apple, Orange, Pear

Les méthodes push et unshift peuvent ajouter plusieurs éléments à la fois :

let fruits = ["Apple"];

fruits.push("Orange", "Peach");
fruits.unshift("Pineapple", "Lemon");

// ["Pineapple", "Lemon", "Apple", "Orange", "Peach"]
alert( fruits );

Les internes

Un tableau est un type d’objet spécial. Les crochets utilisés pour accéder à la propriété arr[0] proviennent en fait de la syntaxe de l’objet. C’est essentiellement la même chose que obj[key], où arr est l’objet, tandis que les nombres sont utilisés comme clés.

Ils étendent les objets en fournissant des méthodes spéciales pour travailler avec des collections ordonnées de données ainsi que la propriété length. Mais au fond c’est toujours un objet.

N’oubliez pas qu’il n’y a que huit types de base en JavaScript (voir le chapitre Les types de données pour plus d’infos). Array est un objet et se comporte donc comme un objet.

Par exemple, il est copié par référence :

let fruits = ["Banana"]

let arr = fruits; // copier par référence (deux variables font référence au même tableau)

alert( arr === fruits ); // true

arr.push("Pear"); // modifie le tableau par référence

alert( fruits ); // Banana, Pear - 2 items maintenant

…Mais ce qui rend les tableaux vraiment spéciaux, c’est leur représentation interne. Le moteur tente de stocker ses éléments dans une zone de mémoire contiguë, l’un après l’autre, exactement comme le montrent les illustrations de ce chapitre. Il existe également d’autres optimisations permettant de faire fonctionner les tableaux très rapidement.

Mais ils se cassent tous si nous arrêtons de travailler avec un tableau comme avec une “collection ordonnée” et commençons à le travailler comme s’il s’agissait d’un objet normal.

Par exemple, techniquement, nous pouvons faire ceci :

let fruits = []; // créer un tableau

fruits[99999] = 5; // assigne une propriété avec un index beaucoup plus grand que sa longueur

fruits.age = 25; // créer une propriété avec un nom arbitraire

C’est possible, car les tableaux sont des objets à leur base. Nous pouvons leur ajouter des propriétés.

Mais le moteur verra que nous travaillons avec le tableau comme avec un objet normal. Les optimisations spécifiques à un tableau ne sont pas adaptées à ce type de situation et seront désactivées. Leurs avantages disparaissent.

Les moyens de casser un tableau :

  • Ajouter une propriété non numérique comme arr.test = 5.
  • Faire des trous, comme ajouter arr[0] et ensuite arr[1000] (et rien entre eux).
  • Remplire le tableau dans l’ordre inverse, comme arr[1000], arr[999] etc.

Veuillez considérer les tableaux comme des structures spéciales pour travailler avec les données ordonées. Ils fournissent des méthodes spéciales pour cela. Les tableaux sont soigneusement réglés dans les moteurs JavaScript pour fonctionner avec des données ordonnées contiguës, veuillez les utiliser de cette manière. Et si vous avez besoin de clés arbitraires, il y a de fortes chances pour que vous ayez réellement besoin d’un objet régulier {}.

Performance

Les méthodes push/pop vont vite, alors que shift/unshift sont lentes.

Pourquoi est-il plus rapide de travailler avec la fin d’un tableau qu’avec son début ? Voyons ce qui se passe pendant l’exécution :

fruits.shift(); // prends 1 élément du début

Il ne suffit pas de prendre l’élément avec le nombre 0. D’autres éléments doivent également être renumérotés.

L’opération shift doit faire 3 choses :

  1. Supprimer l’élément avec l’index 0.
  2. Déplacer tous les éléments à gauche, les renuméroter de l’index 1 à 0, de2 à 1, etc.
  3. Mettre à jour la propriété length.

Plus il y a d’éléments dans le tableau, plus il y faut de temps pour les déplacer, plus il y a d’opérations en mémoire.

La même chose se produit avec unshift. Pour ajouter un élément au début du tableau, nous devons d’abord déplacer les éléments existants vers la droite, en augmentant leur index.