Les mots-clés let et const
Un besoin de block-scoping
On l’avait vu, en JavaScript, on déclarait des variables avec var
.
Celles-ci étaient function-scopées, c’est à dire qu’une déclaration était valide au niveau d’une fonction.
function test() { var hello = 'hello'; if (hello) { var i = 0; } console.log(i); // 0 (i est déclaré !) }
Si cette limite était acceptable quand JavaScript était un langage destiné à faire des scripts simples, elle pose bien plus problème en commençant à faire des applications complexes.
Toute variable déclarée dans une boucle par exemple, restait valide en dehors de celle-ci pouvant créer des effets de bord.
Pour contrer ça, on avait l’habitude de wrapper certaines parties du code dans des fonctions anonymes exécutées immédiatement (IIFE, pour Immediately Invoked Function Expression).
function hello() { var obj = { a: 3, b: 6 }; // Key et value sont locales à cette boucle (function () { var key, value; for (key in obj) { value = obj[key]; // do sth with value... } })(); }
Deux mots clés pour gouverner var
Pour l’ES6, sorti officiellement en 2015, l’ECMA a standardisé deux nouveaux mots clés, let
et const
, pouvant déclarer une variable au niveau du bloc.
function test() { let hello = 'hello'; if (hello) { let i = 0; } console.log(i); // ReferenceError: i is not declared }
Cela résout également notre problème de variable locale à une boucle.
function hello() { let obj = { a: 3, b: 6 }; for (let key in obj) { let value = obj[key]; // key et value sont locales à cette boucle ! } }
Alors, quelle différence entre let
et const
? const
restreint l’affectation : une variable déclarée avec const
ne peut être affectée qu’à l’initialisation. C’est dire qu’à aucun autre endroit qu’à l’initialisation de votre variable, vous ne pouvez écrire ma_const = *quelquechose*
.
Contrairement à ce qu’on peut penser, const
ne rend pas la valeur constante, elle signifie en fait que la variable ne peut pas changer de référence au cours de sa vie.
const ma_const = 3; // TypeError ma_const = 4; const mon_obj = {}; // Ok ! mon_obj ne change pas de référence mon_obj.hello = 4;
On prendra pour habitude d’utiliser const
dès qu’on sait que la variable ne changera pas de valeur, par convention. let
est utilisé uniquement quand on utilise des scalaires / variables allant muter (des nombres étant incrémentés, des string
allant être modifiées, etc).
let
et const
sont des modifications simples et rapides à expliquer, mais elles changent fondamentalement le langage 🙂 !
Les nouvelles structures de données, Map et Set
Structures de données usuelles en programmation, les Map
et les Set
étaient, jusqu’à l’ES6 (2013-2015), inconnues au bataillon.
C koi ?
Map
Une Map
est une structure de données associant une clé à une valeur. La structure, utilisant généralement une table de hashage pour stocker les clés, permet de retrouver une valeur rapidement étant donné une clé, et garantit qu’une clé ne puisse être dupliquée dans la structure.
L’opération d’accession (= retrouver la valeur associée à une clé) est généralement d’une complexité approchant la complexité constante : ce qui signifie que peu importe le nombre de clés, la structure mettra le même temps à retrouver votre valeur étant donné une clé donnée.
En Python, un équivalent serait le dict
:
un_objet = open('file.txt', 'r') ### La map est ici # On peut stocker n'importe quoi en clé comme en valeur my_dict = { 'key': 'value', 3: 'hello', un_objet: 'valeur associée à l\'objet' }
Cette structure existe vraiment partout, même dans la STL du C++ :
#include <map> #include <string> std::map<int, std::string> number_to_string; number_to_string[3] = "hello"s; number_to_string[18] = "eighteen"s;
Set
Un Set
est une structure de données permettant de stocker un nombre illimités de valeurs toutes distinctes entre elles.
Le principe d’un set est de garantir l’unicité des valeurs, et de pouvoir tester très rapidement si une valeur est présente ou non (à complexité quasi-constante, idéalement).
De la même manière, un tel objet existait déjà en Python :
allowed_numbers = { 1, 3, 5, 17 } if 3 in allowed_numbers: print('3 is allowed !') # S'affichera if 18 in allowed_numbers: print('18 is also allowed') # Ne s'affichera pas
Comment on faisait, avant ?
En JavaScript
, nous avions néanmoins la possibilité de simuler ces deux structures de données pour les string
uniquement en utilisant les objets. En effet, les objets de JS sont des conteneurs associant clé => valeur, avec une condition : la clé doit être une string
.
L’usage faisait que les objets natifs de JS étaient utilisés en tant que Map
ou Set
basique.
Voici un example très simple qui encapsulait un objet dans une "classe" Map
avant l’ES6 :
// On construit notre map très basique function Map() { this.content = {}; } Map.prototype.get = function (key) { return this.content[key]; }; Map.prototype.set = function (key, value) { return this.content[key] = value; }; Map.prototype.has = function (key) { return this.content.hasOwnProperty(key); }; Map.prototype.delete = function (key) { delete this.content[key]; }; Map.prototype.keys = function () { var key = '', keys = []; for (key in this.content) { if (this.has(key)) { keys.push(key); } } return keys; }; Map.prototype.values = function () { var key = '', values = []; for (key in this.content) { if (this.has(key)) { values.push(this.get(key)); } } return values; }; Map.prototype.size() = function () { var i = 0, key = ''; for (key in this.content) { if (this.content.hasOwnProperty(key)) { i++; } } return i; }; // On se sert de notre map ! var map = new Map(); map.set('hello', 'world'); map.has('hello'); // true map.get('hello'); // 'world' map.keys(); // ['hello'] map.values(); // ['world'] map.size(); // 1
Et maintenant, une encapsulation d’un Set
qui utilise notre Map
:
function Set() { this.content = {}; } Set.prototype.keys = Map.prototype.keys; Set.prototype.size = Map.prototype.size; Set.prototype.has = Map.prototype.has; Set.prototype.delete = Map.prototype.delete; Set.prototype.add = function (key) { this.content[key] = null; }; // On peut se servir du Set ! var set = new Set(); set.add('hello'); set.has('hello'); // true // ...
Ces deux types de structures restent néanmois très limités. Pour la Map
, les clés sont limités à des string
, tandis que pour le Set
, les valeurs stockées sont elles aussi limitées au string
.
Alors, pour cela, Map
et Set
natifs à la rescousse !
Les nouveaux Map et Set
Les objets plus haut disposent volontairement de la même API que les nouveaux objets natifs, à la différence près de .keys()
et .values()
.
Les clés / valeurs peuvent être de n’importe quel type dans les nouveaux objets, cependant, lorsqu’on utilise des non-primitives, la vérification de l’existance se réalise à l’aide de la référence de l’objet. Explications.
// Les nouvelles Map ES6 const map = new Map(); map.set('hello', 'world'); const obj1 = { a: 3 }; const obj2 = { a: 3 }; obj1 === obj2; // false, même contenu mais références différentes map.set(obj1, 'b'); map.get(obj1); // 'b', ça fonctionne ! map.get(obj2); // undefined, obj2 n'est lié à rien // Les sets fonctionnent comme les maps, mais sans les valeurs const unique = new Set(); unique.add(obj1); unique.has(obj1); // true unique.has(obj2); // false // Il est possible d'itérer les clés/valeurs // Ce sont des itérateurs, des objets qui seront abordés un peu plus tard const values = unique.values(); let current = values.next(); while (!current.done) { const value = current.value; // Do something with value... current = values.next(); }
Malheureusement, il n’est pas possible de personnaliser la manière dont les objets sont comparés pour assurer leur unicité. Les primitives (+ les string
) sont comparées par valeur, tandis que les objets sont comparés par référence. Il n’y a pas de système de hash
, comme en Java ou Python par exemple.
À la fin de ce résumé, il y aura un petit bonus où l’on construira ensemble une Map
+ Set
capable d’utiliser des hash définis par des utilisateurs sur des objets :).
La fin
Cela en est fini pour cette partie ! Dans la suivante, on discute des nombreuses nouveautés autour des objets, la réelle primitive de JavaScript, apportées depuis 2008. Jetez-y un coup d’oeil !
Article précédent : Modules • Article suivant : Des objets plus puissants