vendredi, 31 juillet 2015

ES6 en détails : les classes

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 à Jérémie et Banban pour la traduction et à goofy pour 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é).

Aujourd’hui, retour aux choses simples après quelques billets précédents assez complexes. Pas de façon complètement nouvelle d’écrire du code avec des générateurs, pas d’objets Proxy superpuissants qui viendraient s’accrocher à l’algorithmique interne du langage JavaScript, pas de nouvelles structures de données qui vous éviteraient de réaliser vos propres solutions. À la place, nous allons parler d’une solution syntaxique et idiomatique pour résoudre un vieux problème : la création de constructeurs d’objets en JavaScript.

Le problème

Supposons que l’on veuille créer l’exemple le plus emblématique de la conception orientée objet : la classe Cercle. Imaginons que nous sommes en train de créer un Cercle pour une petite bibliothèque Canvas. Entre autres choses, on pourrait avoir envie de savoir comment réaliser les opérations suivantes :

  • Dessiner un Cercle donné dans un Canvas donné.
  • Se souvenir du nombre de Cercles que l’on a créés jusque-là.
  • Se souvenir du rayon d’un Cercle en particulier et garantir qu’il ne puisse être modifié.
  • Calculer la surface d’un Cercle donné.

JavaScript tel qu’on le connait actuellement nous indique que l’on doit tout d’abord créer le constructeur de cet objet sous forme d’une fonction. Ensuite, il est nécessaire d’ajouter toutes les propriétés que l’on pourrait vouloir appliquer à notre objet directement sur cette fonction. Enfin, il va falloir remplacer la propriété prototype de ce constructeur par un objet ad hoc. Cet objet prototype contiendra toutes les méthodes nécessaires aux instances de notre classe. Même pour un exemple très simple, une fois que vous en aurez fini, vous allez vous retrouver avec ce genre de code un peu écœurant.

function Cercle(rayon) {
    this.rayon = rayon;
    Cercle.nbrDeCercles++;
}

Cercle.dessiner = function dessiner(cercles, canvas) { /* Code pour dessiner dans le Canvas */ }
Object.defineProperty(Cercle, "nbrDeCercles", {
    get: function() {
        return !this._count ? 0 : this._count;
    },

    set: function(val) {
        this._count = val;
    }
});

Cercle.prototype = {
    surface: function surface() {
        return Math.pow(this.rayon, 2) * Math.PI;
    }
};
Object.defineProperty(Cercle.prototype, "rayon", {
    get: function() {
        return this._radius;
    },

    set: function(rayon) {
        if (!Number.isInteger(rayon))
            throw new Error("Le rayon du cercle doit être un entier.");
        this._radius = rayon;
    }
});

Non seulement le code est lourdingue mais il est égalment assez peu intuitif. Il nécessite d’avoir une excellente compréhension du fonctionnement des fonctions et de la façon dont les propriétés et méthodes sont utilisables par les instances d’objets. Cela vous semble compliqué ? Pas d’inquiétude. Tout l’enjeu de cet article est de vous montrer une methode bien plus simple pour écrire un code équivalent.

Syntaxe de définition des méthodes

Pour commencer à nettoyer tout ça, ES6 propose une nouvelle syntaxe afin d’ajouter des propriétés exotiques à un objet. S’il a été assez facile d’ajouter la méthode surface à l’objet Cercle.prototype ci-avant, ça n’a pas été une mince affaire de gérer les getters/setters de la propriété rayon. Dans la mesure où JavaScript est de plus en plus utilisé avec une approche orientée objet, de nombreuses personnes ont eu envie de définir des méthodes plus simples pour ajouter de tels accesseurs et mutateurs. Ce dont nous avions besoin, c’était une façon d’ajouter des « méthodes » à un objet aussi simple que obj.prop = methode, sans la lourdeur de Object.defineProperty. On veut pouvoir simplement:

  • Ajouter une fonction ordinaire comme propriété d’un objet
  • Ajouter une fonction générateur comme propriété d’un objet
  • Ajouter une fonction accesseur ou mutateur comme propriété d’un objet
  • Ajouter n’importe laquelle des fonctions ci-avant comme si l’on avait utilisé la syntaxe à crochets [] sur l’objet fini, ce que l’on appelle les « noms de propriétés générés ».

Certaines de ces actions étaient impossibles jusqu’à présent. Par exemple, il n’y avait aucun moyen de définir un accesseur ou un mutateur via une affectation directe à obj.prop. Il a donc fallu rajouter une nouvelle syntaxe. Désormais, vous pouvez écrire le code suivant :

var obj = {
    // Les méthodes sont désormais ajoutées sans le mot clé "function",
    // le nom de la propriété devenant le nom de la fonction.
    methode(args) { ... },

    // Pour créer une méthode qui soit un générateur, ajoutez juste un '*', comme d'habitude.
    *genMethode(args) { ... },

    // Les accesseurs et mutateurs peuvent désormais être créés directement sur place
    // avec l'aide de |get| et |set|. Cependant ils  ne peuvent pas être des générateurs.

    // Notez qu'un accesseur défini de cette façon ne doit avoir aucun argument.
    get propName() { ... },

    // Notez qu'un mutateur défini de cette façon doit avoir exactement un argument.
    set propName(arg) { ... },

    // Pour pouvoir gérer le quatrième cas ci-avant, la syntaxe à crochets [] est autorisée
    // partout où un nom de fonction est attendu. Cela permet d'utiliser des symboles,
    // des appels de fonction, des concaténations de chaînes et toute autre méthode pouvant
    // être évaluée comme un identifiant de propriété valide. L'exemple ci-après crée une 
    // méthode mais cela fonctionne également pour les accesseurs, mutateurs et générateurs.
    [functionQuiRenvoieUnNomDePropriété()] (args) { ... }
};

En utilisant cette nouvelle syntaxe, on peut reécrire notre exemple de la manière suivante :

function Cercle(rayon) {
    this.rayon = rayon;
    Cercle.nbrDeCercles++;
}

Cercle.dessiner = function dessiner(cercles, canvas) {
 /* Code pour dessiner dans le Canvas */
}

Object.defineProperty(Cercle, "nbrDeCercles", {
    get: function() {
        return !this._count ? 0 : this._count;
    },

    set: function(val) {
        this._count = val;
    }
});

Cercle.prototype = {
    area() {
        return Math.pow(this.rayon, 2) * Math.PI;
    },

    get rayon() {
        return this._radius;
    },

    set rayon(radius) {
        if (!Number.isInteger(radius))
            throw new Error("Le rayon du cercle doit être un entier.");
        this._radius = radius;
    }
};

Si vous voulez pinailler, ce code n’est pas tout à fait identique au précédent. Les méthodes définies via la notation littérale sont configurables et énumérables, alors que les accesseurs et mutateurs définis précédemment ne sont ni configurables ni énumérables. Dans les faits, c’est quelque chose que l’on remarque rarement et j’ai décidé d’éluder cette question pour rester simple.

Quoi qu’il en soit, c’est déjà beaucoup mieux, n’est-ce pas ? Malheureusement, même armé de cette nouvelle syntaxe, on ne peut pas faire grand-chose pour la définition de Cercle puisque l’on doit toujours définir une fonction. Or, il n’est pas possible de définir les propriétés de cette fonction pendant qu’on définit la fonction elle-même.

Syntaxe de définition des classes

Bien que se soit mieux, ça ne satisfait toujours pas les personnes qui veulent un solution plus simple pour la conception orientée objet en JavaScript. Leur argument est le suivant : les autres langages possèdent une structure faite pour gérer la conception orientée objet : les classes

Ok. Ajoutons-donc les classes.

Ce que l’on veut, c’est un mécanisme qui nous permettra d’ajouter des méthodes à un constructeur identifié et d’ajouter des méthodes à son prototype, méthodes qui seront donc accessibles aux instances de la classe. Vu qu’on a déjà notre nouvelle syntaxe de définition des méthodes, autant l’utiliser. On a juste besoin d’un moyen de différencier les méthodes génériques, utilisables pour toutes les instances de classe, et les méthodes spécifiques à chaque instance. En C++ ou en Java, le mot-clé pour faire cette différence c’est static. Il en vaut bien un autre, utilisons celui-ci.

À présent, il serait bien utile d’avoir un moyen pour identifier la méthode qui, parmi toutes les autres, sera le constructeur de la classe. En C++ ou en Java, cette fonction a le même nom que la classe sans type de retour. Puisque JavaScript n’a, de toute façon, pas de type de retour et que l’on a besoin d’une propriété constructor pour des questions de rétro-compatibilité, on va appeler cette méthode constructor.

En faisant tout ça, on peut réécrire notre classe Cercle comme elle aurait toujours dû l’être :

class Cercle {
    constructor(rayon) {
        this.rayon = rayon;
        Cercle.nbrDeCercles++;
    };

    static dessiner(cercle, canvas) {
        // Code pour dessiner dans le Canvas
    };

    static get nbrDeCercles() {
        return !this._count ? 0 : this._count;
    };
    static set nbrDeCercles(val) {
        this._count = val;
    };

    surface() {
        return Math.pow(this.rayon, 2) * Math.PI;
    };

    get rayon() {
        return this._radius;
    };
    set rayon(radius) {
        if (!Number.isInteger(radius))
            throw new Error("Le rayon du cercle doit être un entier.");
        this._radius = radius;
    };
}

Wow ! Non seulement, nous avons pu regrouper tout ce qui est propre à notre Cercle mais en plus c’est si… simple. C’est clairement mieux que ce que nous avions au départ. Malgré tout, vous allez sans doute avoir des questions ou bien vous allez trouver certains cas particuliers. J’ai fait de mon mieux pour anticiper et répondre à certains d’entre eux :

  • Pourquoi des point-virgules ? — Dans une tentative de faire en sorte que ça ressemble à des classes « traditionnelles », nous avons choisi d’utiliser un séparateur classique. Vous n’aimez pas ça ? C’est optionnel, les delimiteurs ne sont pas obligatoires.
  • Comment faire si je ne veux pas de constructeur mais que je veux quand même ajouter des méthodes à une classe ? — Pas de problème. La méthode constructor est complètement facultative. Si vous n’en définissez aucune, ça va se comporter comme si vous aviez écrit constructor() {}.
  • Un constructeur peut-il être un générateur ? — Nope ! Ajouter un constructeur qui n’est pas une fonction normale engendrera une erreur TypeError. Ça vaut pour les générateurs mais également pour les accesseurs et mutateurs.
  • Puis-je définir un constructeur via un nom de propriété généré ? — Hélas non. C’est vraiment difficile a gérer, on n’a donc même pas essayé. Si vous définissez une méthode avec un nom de propriété généré qui se trouve être constructor, vous obtiendrez bien une methode appelé constructor mais ce ne sera pas le constructeur de la classe.
  • Que se passe-t-il si je change la valeur de Cercle ? Cela posera-t-il des problèmes si j’utilise new Cercle ? Non ! Comme pour les expressions de fonctions, les classes reçoivent une structure interne pour un nom donné. Cette structure ne peut pas être modifiée depuis l’extérieur, quelle que soit la valeur utilisée pour modifier la variable Cercle dans la portée courante. Cercle.nbrDeCercles++ du constructeur continuera de fonctionner normalement.
  • Certes, mais je pourrais passer un littéral objet directement comme argument d’une fonction. Ces nouvelles « classes » ne fonctionneraient plus, non ? – Heureusement, ES6 apporte également les expressions de classe. Celles-ci peuvent être nommées ou anonymes et elles se comporteront exactement comme ce qu’on a vu avant sauf qu’elles ne créeront pas de variable dans la portée de la déclaration.
  • Et au fait, qu’en est-il de l’énumérabilité et du reste ? – Les gens souhaitaient pouvoir installer des méthodes sur des objets mais n’obtenir que les propriétés de données lors d’une énumération, ce qui est logique. Pour cette raison, les méthodes ajoutées aux classes sont configurables mais pas énumérables.
  • Euh, attendez ? Où sont mes variables d’instances et mes constantes statiques ? – Bien vu. À l’heure actuelle, elles n’existent pas avec les classes ES6. Mais c’est bien parti pour les avoir par la suite : le sujet a déjà été abordé lors des réunions de spécifications par moi et plusieurs personnes favorables à l’idée d’avoir à la fois des valeurs statiques et des constantes utilisables avec cette syntaxe de classe. D’autres discussions sont à venir sur ce sujet.
  • OK, ça a l’air super ! Puis-je utiliser cette fonctionnalité ? – Pas exactement. Certaines prothèses existent (notamment grâce à Babel) et vous pouvez actuellement vous amuser avec les classes. Malheureusement, cela va prendre encore un peu de temps avant qu’elles ne soient implémentées au sein des principaux navigateurs. Tout ce dont nous avons parlé aujourd’hui a été implémenté par votre serviteur et est disponible dans la version Nightly de Firefox. Les classes sont implémentées dans Edge et Chrome mais ne sont pas activées par défaut. Il semblerait qu’à l’heure actuelle, il n’y ait pas d’implémentation pour Safari.
  • Java et C++ permettent de créer des sous-classes et utilisent le mot-clé super. Cet article n’en parle pas, est-ce que JavaScript permet de faire pareil ? Oui, toutefois c’est un sujet suffisamment vaste pour un autre billet. Nous reviendrons prochainement pour parler des sous-classes et explorer le pouvoir des classes JavaScript.

Je n’aurais pas été capable d’implémenter les classes sans l’aide apportée par Jason Orendorff et Jeff Walden et la relecture de code qu’ils ont effectuée.

La semaine prochaine, Jason Orendorff reviendra pour expliquer en détails les nouvelles instructions ES6 let et const.

dimanche, 26 juillet 2015

MDN : dix ans d'évolution

MDN-10years_twitter-avatar_400x400px.png

MDN fête ses 10 ans cette semaine. Ce billet, traduction du billet de Janet Swisher, est l’occasion de retracer l’historique de MDN et d’expliquer l’orientation du projet aujourd’hui.


Lire la suite

vendredi, 24 juillet 2015

ES6 en détails : les proxies

power-plant.jpg

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 à Ilphrin et Banban pour la traduction et la relecture !


Lire la suite

jeudi, 16 juillet 2015

ES6 en détails : les générateurs, la suite

img1.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.

Merci à goofy et Banban pour la relecture !


Lire la suite

mardi, 7 juillet 2015

ES6 en détails : les collections

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, Banban, Ilphrin et goofy pour la traduction et la relecture !


Lire la suite

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 !


Lire la suite

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

- page 1 de 2