dimanche, 21 juin 2015

ES6 en détails : les symboles

Suite de la traduction, qui continue la série d’articles de Jason Orendorff. L’article original se trouve ici. Vous pouvez retrouver les différents articles de la série grâce aux mots-clefs.

Merci à Marine, Mentalo, Banban, Thegennok et goofy pour la traduction et la relecture !


ES6 en détails est une série d’articles décrivant les nouvelles fonctionnalités ajoutées au langage de programmation JavaScript avec la sixième édition du standard ECMAScript (ES6 en abrégé).

Que sont les symboles ES6 ?

Les symboles ne sont pas des logos. Ce ne sont pas de petits dessins que vous utilisez dans votre code.

let  =   ♪ × ♫;  // SyntaxError

Il ne s’agit pas non plus d’un procédé littéraire utilisé pour représenter quelque chose d’autre.

Malgré une légère consonance, les symboles ne sont pas non plus des cymbales.

Alors que sont les symboles ?

Rencontre du septième type

Depuis la première standardisation de JavaScript en 1997, il y a toujours eu six types de variables. Jusqu’à ES6, chaque valeur d’un programme écrit en JS rentrait dans l’une de ces catégories :

  • Undefined
  • Null
  • Boolean
  • Number
  • String
  • Object

Chaque type contient un ensemble de valeurs. Les cinq premiers ensembles sont finis. Il y a, bien sûr, seulement deux valeurs booléennes, true et false, on ne va pas en inventer d’autres. Il y a bien plus de valeurs de type Number et String. D’après le standard, il est possible de représenter 18 437 736 874 454 810 627 nombres différents (en incluant NaN, le nombre qui a pour nom « Not a Number », « Pas un Nombre » en français). Ce n’est rien comparé au nombre de chaînes de caractères (String) possible, qui vaut d’après moi (2144,115,188,075,855,872 − 1) ÷ 65,535… mais j’ai peut-être mal compté.

L’ensemble des valeurs pour les objets (type Object) est infini. Chaque objet est unique, un peu comme un flocon de neige. Chaque fois que vous ouvrez une page web, de nombreux objets sont créés.

Les symboles ES6 sont des valeurs, mais ce ne sont pas des chaînes de caractères (String). Ce ne sont pas des objets. C’est une nouveauté : un septième type de valeurs.

Nous allons prendre un exemple pour les étudier.

Un simple petit booléen

Parfois, on aimerait tout simplement pouvoir stocker des données additionnelles dans un objet JavaScript appartenant à quelqu’un d’autre.

Par exemple, supposons que vous écriviez une bibliothèque JS qui utilise les transitions CSS pour déplacer des éléments DOM sur l’écran. Vous avez sans doute remarqué qu’appliquer plusieurs transitions CSS à un seul élément div ne fonctionne pas. C’est moche, ça « saute » sans arrêt. Vous pensez pouvoir arranger ça, mais d’abord vous devez trouver un moyen de savoir si un élément est déjà en mouvement.

Comment y arriver ?

Une solution serait d’utiliser une API CSS pour demander au navigateur si l’élément bouge. Mais cela paraît disproportionné. Votre bibliothèque devrait déjà savoir quel élément se déplace : c’est bien le code de la bibliothèque qui a mis l’élément en mouvement au début !

Ce que vous voulez vraiment, c’est garder une trace des éléments qui bougent. Vous pourriez définir un tableau contenant les éléments qui bougent. Chaque fois que votre bibliothèque anime un élément, vous pourriez alors vérifier si cet élément existe déjà dans le tableau.

Hmm… Cela entraînera donc une recherche linéaire, ce qui peut s’avérer lent si le tableau est long.

En fait, ce que vous voulez vraiment, c’est pouvoir associer un état à l’élément :

if (element.isMoving) {
  smoothAnimations(element);
}
element.isMoving = true;

Cela peut éventuellement poser plusieurs problèmes. Tous ces problèmes reposent sur le fait que votre code n’est pas le seul élément à utiliser le DOM.

  1. Un autre code utilisant for-in ou Objects.keys() pourra trébucher sur la propriété que vous avez créée.
  2. Un concepteur ingénieux travaillant sur la bibliothèque peut avoir déjà pensé à cette technique, ce qui pourra détériorer les interactions avec votre bibliothèque.
  3. Un autre concepteur pourra penser à cela plus tard, ce qui pourra détériorer les interactions avec votre bibliothèque plus tard.
  4. Le comité de standardisation pourrait décider d’ajouter une méthode .isMoving() à tous les éléments, et là vous seriez vraiment mal !

Évidemment, vous pouvez résoudre les trois derniers points en choisissant une chaîne tellement complexe et folle que personne d’autre ne pourra jamais nommer quoi que ce soit de la même façon :

if (element.__$jorendorff_animation_library$NON_PAS_TOUCHE_A_CETTE_PROPRIETE_BISOUS$isMoving__) {
  smoothAnimations(element);
}
element.__$jorendorff_animation_library$NON_PAS_TOUCHE_A_CETTE_PROPRIETE_BISOUS$isMoving__ = true;

Cette solution ne mérite même pas un coup d’œil.

Vous pourriez aussi générer un nom quasiment unique pour cette propriété grâce à la cryptographie :

// obtenir un gloubi-boulga de 1024 caractères Unicode
var isMoving = SecureRandom.generateName();
...
if (element[isMoving]) {
  smoothAnimations(element);
}
element[isMoving] = true;

La syntaxe objet[nom] permet littéralement d’utiliser n’importe quelle chaîne pour dénommer une propriété, ceci fonctionnera et les collisions seront virtuellement impossibles. De plus, votre code est lisible.

En revanche, bonne chance pour le débogage. Chaque fois que vous utiliserez console.log() avec cette propriété, vous obtiendrez une longue chaîne illisible. Et si vous avez besoin de plusieurs propriétés comme celle-ci ? Comment allez-vous les gérer ? Elles auront des noms différents à chaque fois que la page sera rechargée.

Pourquoi est-ce aussi compliqué ? On veut juste un petit booléen !

Tout un symbole

Les symboles sont des valeurs que les programmes peuvent créer et utiliser comme des clés de propriétés sans risquer de rentrer en collision avec les noms déjà utilisés.

var monSymbole = Symbol();

Appeler Symbol() crée un nouveau symbole, c’est-à-dire une valeur qui n’est égale à aucune autre valeur. Un symbole peut être utilisé pour être la clé d’une propriété, comme une chaîne ou un nombre. Étant donné qu’un symbole n’est égal à aucune chaîne, une propriété désignée par un symbole n’entrera pas en collision avec une autre propriété.

obj[monSymbole] = "ok !";       // garanti sans collision
console.log(obj[monSymbole]);   // ok !

Voici comment vous pouvez utiliser un symbole pour la situation dont nous avons discuté ci-avant :

// On crée un symbole unique
var isMoving = Symbol("isMoving");
...
if (element[isMoving]) {
  smoothAnimations(element);
}
element[isMoving] = true;

Quelques notes à propos de ce code :

  • La chaîne "isMoving" dans Symbol("isMoving") est appelée une description. C’est utile lors du débogage. Elle est visible quand on affiche le symbole sur la console via console.log(), quand on le convertit en chaîne avec .toString(), et sans doute aussi dans les messages d’erreurs. C’est tout.
  • element[isMoving] est une propriété dont la clé est un symbole. C’est seulement une propriété dont le nom est un symbole au lieu d’être une chaîne. À part ça, c’est une propriété tout à fait classique.
  • Comme pour les éléments d’un tableau, il est impossible d’accéder aux propriétés dont la clé est un symbole avec la syntaxe littérale utilisant le point (objet.nom). Il faut utiliser les crochets.
  • Il est extrêmement simple d’accéder à une propriété dont la clé est un symbole si vous connaissez le symbole. L’exemple ci-dessus montre comment créer element[isMoving] et y accéder. On pourrait aussi tester if(isMoving in element) voire supprimer element[isMoving] si nécessaire.
  • D’un autre côté, tout ceci n’est possible que si isMoving appartient à la portée. Ainsi les symboles permettent une encapsulation faible : un module qui crée quelques symboles pour lui-même peut les utiliser sur n’importe quel objet, sans craindre les collisions avec des propriétés créées par un autre code.

Les symboles ont été conçus pour éviter les collisions. Pour cette raison, les méthodes classiques pour explorer les objets ignorent les clés qui sont des symboles. La boucle for-in, par exemple, itérera uniquement sur les clés d’un objet qui sont des chaînes de caractères. Les clés qui sont des symboles seront ignorées. Il en va de même pour Object.keys(obj) et Object.getOwnPropertyNames(obj). Cependant, les symboles ne sont pas privés ou invisibles : la nouvelle API, Object.getOwnPropertySymbols(obj), liste les clés d’un objet qui sont des symboles. Une autre API, Reflect.ownKeys(obj), renvoie les clés qui sont des chaînes et des symboles (cette API sera l’objet d’un billet détaillé à venir).

Les différents frameworks et bibliothèques auront vraisemblablement de nombreux cas d’utilisations pour les symboles. Comme nous allons le voir par la suite, le langage lui-même tire parti des symboles pour différents scénarios.

Mais qu’est-ce qu’un symbole ?

> typeof Symbol()
"symbol"

Les symboles ne ressemblent à aucun autre type pré-existant.

Une fois qu’ils sont créés, ils sont immuables. Il est impossible de définir des propriétés sur eux (si vous essayez en mode strict, vous aurez une TypeError). Ils peuvent être utilisés comme noms pour des propriétés. En ce sens, ils ressemblent à des chaînes de caractères.

D’un autre côté, chaque symbole est unique et se distingue des autres symboles (y compris de ceux qui ont la même description) et on peut facilement en créer des nouveaux. En ce sens, ils ressemblent à des objets.

Les symboles ES6 sont semblables aux symboles traditionnels qu’on peut trouver dans les langages tels que Lisp et Ruby. Cependant, ils ne sont pas si profondément ancrés dans les langages. En Lisp, tous les identifiants sont des symboles. En JS, la plupart des identifiants et la plupart des clés restent des chaînes de caractères, les symboles ne sont qu’une option supplémentaire.

Attention, à la différence des symboles dans les autres langages, les symboles JS ne peuvent pas être convertis automatiquement en chaînes de caractères. Si vous essayez de concaténer un symbole et une chaîne de caractères, vous obtiendrez une exception TypeError.

> var sym = Symbol(">3");
> "votre symbole est " + sym
// TypeError: can't convert symbol to string
> `votre symbole est ${sym}`
// TypeError: can't convert symbol to string

Pour éviter cela, on peut convertir le symbole en une chaîne de façon explicite avec String(sym) ou sym.toString().

Trois ensembles de symboles

Il y a trois façons d’obtenir un symbole :

  • Appeler Symbol(). Comme nous l’avons vu avant, cette fonction renvoie un nouveau symbole à chaque fois qu’elle est appelée.
  • Appeler Symbol.for(string). Cela permet d’accéder à un ensemble de symboles existants, appelé registre des symboles. À la différence des symboles uniques définis avec Symbol(), les symboles appartenant au registre sont partagés. Si vous invoquez Symbole.for("chat") trente fois, vous obtiendrez toujours le même symbole à chaque fois. Le registre peut s’avérer utile quand plusieurs pages web, ou plusieurs modules au sein d’une même page web ont besoin de partager un symbole.
  • Utiliser les symboles « connus » définis par le standard tels que Symbol.iterator. Quelques symboles sont définis dans le standard ECMAScript, chacun possède une raison d’être précise.

Si vous avez encore des doutes sur l’utilité des symboles, la dernière catégorie est intéressante car elle illustre comment les symboles ont déjà pu être utilisés en pratique.

Comment ES6 utilise-t-il les symboles connus ?

On a déjà vu un cas utilisation des symboles par ES6 : éviter les collisions avec du code existant. Il y a quelques semaines, dans le billet sur les itérateurs, on a vu que la boucle for (var item of monTableau) démarre en appelant monTableau[Symbol.iterator](). J’ai mentionné que cette méthode aurait pu appeler monTableau.iterator() mais qu’il valait mieux utiliser un symbole pour respecter la rétrocompatibilité.

Maintenant que nous connaissons mieux les symboles, il est plus simple de comprendre pourquoi cela a été fait et ce que ça signifie.

Voici quelques exemples des autres endroits où ES6 utilise les symboles connus (ces fonctionnalités ne sont pas encore implémentées dans Firefox).

  • Rendre instanceof extensible. Avec ES6, l’expression objet instanceof constructeur est définie comme l’appel d’une méthode du constructeur : constructeur[Symbol.hasInstance](objet). Cela signifie qu’on peut étendre instanceof.
  • Éliminer les conflits entre les nouvelles fonctionnalités et du code ancien. On approche ici de la magie noire mais on a découvert que certaines méthodes ES6 pour les tableaux empêchaient certains sites de fonctionner simplement en étant là. D’autres standards du Web ont eu les mêmes problèmes : ajouter de nouvelles méthodes casse les sites existants. Cependant, cette casse est causée par ce qu’on appelle les portées dynamiques. Pour cette raison, ES6 a introduit un symbole spécial : Symbol.unscopables. Celui-ci peut être utilisé par les standard du Web pour éviter que certaines méthodes ne soient impactées par les portées dynamiques.
  • Supporter de nouvelles sortes de correspondances pour les chaînes de caractères. Avec ES5, str.match(monObjet) tentait de convertir monObjet en une RegExp. Avec ES6, le moteur vérifie d’abord si monObjet possède une méthode monObjet[Symbol.match](str). Cela signifie que les bibliothèques peuvent fournir des classes d’analyse de chaînes qui fonctionnent partout où on peut utiliser des objets RegExp.

Chacun de ces cas d’utilisation est relativement restreint et, pour mon code de tous les jours, ça peut être difficile de voir où cela aura un grand impact. Si on prend un peu de recul, c’est plus intéressant : les symboles « connus » de JavaScript peuvent être vus comme une version améliorée des doubles tirets (par exemple __variablePrivée) présents en PHP et Python. Le standard pourra les utiliser à l’avenir pour ajouter de nouveaux éléments au langage, sans risquer de collision avec votre code.

Quand puis-je commencer à utiliser les symboles ES6 ?

Les symboles sont implémentés dans Firefox 36 et Chrome 38. Je les ai moi-même implémentés dans Firefox personnellement, donc si vos symboles se comportent comme des cymbales, vous savez qui contacter.

Pour les navigateurs qui n’ont pas encore implémenté les symboles ES6, vous pouvez utiliser une prothèse telle que core.js. Étant donné que les symboles ne ressemblent pas entièrement à quelque chose d’existant en JS, cette prothèse ne sera pas parfaite. Lisez-bien les mises-en-garde.

La semaine prochaine nous publierons deux billets. D’abord, nous nous intéresserons à des fonctionnalités très attendues qui arrivent enfin en JavaScript avec ES6. Nous démarrerons avec deux fonctionnalités qui datent presque de l’aube de la programmation et nous enchaînerons avec deux autres fonctionnalités, très similaires, mais qui ont été améliorées pour les objets éphémères et les références faibles. Rejoignez-nous la semaine prochaine pour examiner les collections ES6 en détails.

Restez également dans les parages pour un billet supplémentaire de Gastòn Silva sur un sujet qui ne concerne aucune fonctionnalité ES6, mais qui pourrait bien vous fournir le petit coup de pouce nécessaire pour commencer à utiliser ES6 dans vos projets. À bientôt !

ES6 en détails : utiliser ES6 dès aujourd'hui avec Babel et Broccoli

screenshot.png

Suite de la traduction, qui continue la série d’articles de Jason Orendorff. L’article original se trouve ici. Vous pouvez retrouver les différents articles de la série grâce aux mots-clefs.


Lire la suite

dimanche, 14 juin 2015

ES6 en détails : les fonctions fléchées

Suite de la traduction, qui continue la série d’articles de Jason Orendorff. L’article original se trouve ici. Vous pouvez retrouver les différents articles de la série grâce aux mots-clefs.

Merci à Marine, Mentalo, Benjamin, Banban et goofy pour la traduction et la relecture !


Lire la suite

samedi, 6 juin 2015

ES6 en détails : la décomposition

Suite de la traduction, qui continue la série d’articles de Jason Orendorff. L’article original se trouve ici. Vous pouvez retrouver les différents articles de la série grâce aux mots-clefs.

Merci à goofy pour la relecture !


Lire la suite

ES6 en détails : les paramètres du reste et les paramètres par défaut

Suite de la traduction, qui continue la série d’articles de Jason Orendorff. L’article original se trouve ici. Vous pouvez retrouver les différents articles de la série grâce aux mots-clefs.

Merci à Marine et Banban pour la traduction et à goofy pour la relecture !


Lire la suite

dimanche, 31 mai 2015

ES6 en détails : les gabarits de chaînes de caractères

Suite de la traduction, qui continue la série d’articles de Jason Orendorff. L’article original se trouve ici. Vous pouvez retrouver les différents articles de la série grâce aux mots-clefs.

Merci aux traducteurs et relecteurs, Marine, Lucas, Benjamin, Goofy et Benoit :) !


Lire la suite

mercredi, 27 mai 2015

ES6 en détails : les générateurs

Suite de la traduction, qui continue la série d’articles de Jason Orendorff. L’article original se trouve ici.

Merci aux traducteurs et relecteurs :) Marine, Mentalo, Benjamin, Ilphrin et Goofy !


Lire la suite

samedi, 23 mai 2015

ES6 en détails : les itérateurs et la boucle for-of

Suite de la traduction, qui continue la série d’articles de Jason Orendorff. L’article original se trouve ici.

Merci aux traducteurs et relecteurs :) Marine, Mentalo, Benjamin, Amarok, Lucas, Ilphrin et Goofy !


Lire la suite

mardi, 19 mai 2015

ES6 en détails : une introduction

Ce billet est une traduction de cet article, écrit par Jason Orendorff qui participe au développement du moteur JavaScript de Firefox. Ce billet sera le premier d’une série de traductions, chaque billet décrivant une nouvelle fonctionnalité apportée par ECMAScript 6.

Merci à Marine, Mentalo, Lucas et Benjamin pour la traduction :)


Lire la suite

mardi, 23 avril 2013

Première contribution : rapporter un bogue sur Bugzilla

image 1 formulaire grand format

Il est notoire que le bugzilla de Mozilla peut s’avérer difficile à aborder et maîtriser. Pour vous guider dans ce labyrinthe, Liz Henry a rédigé un très bon article illustré sur son blog :  File a bug: the missing manual, now with unicorns. Nous vous le proposons ici en espérant qu’il vous incitera  […]

Lire la suite

jeudi, 14 mars 2013

Firefox OS et l’évolution des API Web - par Brendan Eich

Dans cet article que MozFr a traduit pour vous ci-dessous, Brendan Eich qui est le créateur du JavaScript et l’un des fondateurs de Mozilla fait le point sur le succès qui accompagne le lancement de Firefox OS et sur l’intérêt des nombreuses API pour mobiles que Mozilla propose à la standardisation,  […]

Lire la suite

Optimiser les accès de variables JavaScript

Cet article est issu du blog de Luke Wagner. L’article original a été écrit en anglais par Luke Wagner. J’ai récemment fini un projet visant à améliorer la manière dont SpiderMonkey implémente les accès de variable, ainsi j’ai pensé qu’il était temps d’expliquer comment cela fonctionne désormais. En  […]

Lire la suite

mardi, 19 février 2013

L'assembleur du Web

Re-publication du billet de CLOCHIX sur son blog asm.js est un projet de recherche de Mozilla qui vise à améliorer les performances de JavaScript en n’utilisant qu’un sous-ensemble du langage, plus facile à optimiser. Il se compose de plusieurs sous-projets : la spécification du langage ;  […]

Lire la suite

lundi, 15 octobre 2012

IonMonkey arrive dans Firefox 18

Kraken

Cet article est issu du blog JavaScript de Mozilla. L’article original a été écrit en anglais par David Anderson. Depuis le lundi 12 septembre, IonMonkey, notre nouveau compilateur JavaScript à la volée, est arrivé dans Firefox 18. IonMonkey représente un grand pas en avant pour nos performances sur  […]

Lire la suite

jeudi, 4 octobre 2012

Ramasse-miettes incrémentiel dans Firefox 16

nonincremental.png

Cet article est issu du blog JavaScript de Mozilla. L’article original a été écrit en anglais par Bill McCloskey. Firefox 16 va être la première version à supporter un ramasse-miettes (en anglais Garbage Collector ou GC) incrémentiel. C’est une fonctionnalité majeure, ayant nécessité plus d’un an de  […]

Lire la suite

mercredi, 3 octobre 2012

Présentation du nouveau blog officiel de l'équipe JavaScript de Mozilla

La mission de Mozilla est de «  promouvoir l’ouverture, l’innovation et les possibilités offertes sur la toile  ». Les membres de l’équipe du moteur JavaScript ont une occasion unique de soutenir cette mission. Leur travail passe par des défis techniques tels que la création d’un ramasse-miettes  […]

Lire la suite

vendredi, 27 juillet 2012

about:csswg - Sources d'innovation

Cet article est issue d’une série d’article sur le fonctionnement du CSS Working Group au W3C. L’article original a été écris en anglais par Fantasai. Sources d’innovation Il y a eu de nombreux débats pour savoir si les standards devaient découler des implémentations ou si ce sont les  […]

Lire la suite

mardi, 24 juillet 2012

about:csswg - Processus de spécification


Lire la suite

samedi, 21 juillet 2012

about:csswg - Modularité

Cet article est issue d’une série d’article sur le fonctionnement du CSS Working Group au W3C. L’article original a été écris en anglais par Fantasai. Modularité de CSS Lorsque CSS Level 3 a été créé, il a été pensé comme un ensemble de spécifications modulaires permettant de recréer CSS Level 2 et  […]

Lire la suite

vendredi, 20 juillet 2012

about:csswg - Prise de décisions

Cet article est issue d’une série d’article sur le fonctionnement du CSS Working Group au W3C. L’article original a été écris en anglais par Fantasai. Prise de décisions Comme défini dans le processus du W3C, le CSS WG prend des décisions par consensus. Le consensus dans le CSS WG est généralement  […]

Lire la suite

- page 1 de 2