Quoi de neuf dans ES6 ? Perspective d'une conversion CoffeeScript
Publié: 2022-03-11Je suis fan de CoffeeScript depuis plus de deux ans maintenant. Je trouve que je suis plus productif en écrivant CoffeeScript, je fais moins d'erreurs stupides et, avec la prise en charge des cartes sources, le débogage du code CoffeeScript est totalement indolore.
Récemment, j'ai joué avec ES6 en utilisant Babel, et dans l'ensemble, je suis un fan. Dans cet article, je vais compiler mes découvertes sur ES6 du point de vue d'une conversion CoffeeScript, regarder les choses que j'aime et voir ce qu'il me manque encore.
Indentation syntaxiquement significative : une aubaine ou un fléau ?
J'ai toujours eu une relation amour/haine avec l'indentation syntaxiquement significative de CoffeeScript. Quand tout va bien, vous obtenez le "Regarde ma, pas de mains!" se vanter d'utiliser une syntaxe aussi super-minimaliste. Mais quand les choses tournent mal et qu'une indentation incorrecte provoque de vrais bugs (croyez-moi, ça arrive), on n'a pas l'impression que les avantages en valent la peine.
if iWriteCoffeeScript if iAmNotCareful badThings = 'happen'
Généralement, ces bogues surviennent lorsque vous avez un bloc de code avec quelques instructions imbriquées et que vous indentez accidentellement une instruction de manière incorrecte. Oui, cela est généralement causé par des yeux fatigués, mais à mon avis, cela est beaucoup plus susceptible de se produire dans CoffeeScript.
Quand j'ai commencé à écrire ES6, je n'étais pas sûr de ce que serait ma réaction viscérale. Il s'avère que c'était vraiment agréable de recommencer à utiliser des accolades. L'utilisation d'un éditeur moderne est également utile, car il vous suffit généralement d'ouvrir l'accolade et votre éditeur a la gentillesse de le fermer pour vous. C'était un peu comme retourner dans un univers calme et clair après la magie vaudou de CoffeeScript.
if (iWriteES6) { if (iWriteNestedStatements) { let badThings = 'are less likely to happen' } }
Bien sûr, j'insiste pour supprimer les points-virgules. Si nous n'en avons pas besoin, je dis de les jeter. Je les trouve moches et c'est extra typé.
Soutien de classe
Le support de classe dans ES6 est fantastique, et si vous quittez CoffeeScript, vous vous sentirez comme chez vous. Comparons la syntaxe entre les deux avec un exemple simple :
Classe ES6
class Animal { constructor(numberOfLegs) { this.numberOfLegs = numberOfLegs } toString() { return `I am an animal with ${this.numberOfLegs} legs` } } class Monkey extends Animal { constructor(bananas) { super(2) this.bananas = bananas } toString() { let superString = super.toString() .replace(/an animal/, 'a monkey') return `${superString} and ${this.bananas} bananas` } }
Classe CoffeeScriptCoffeeScript Class
class Animal constructor: (@numberOfLegs) -> toString: -> "I am an animal with #{@numberOfLegs} legs" class Monkey extends Animal constructor: (@numberOfBananas) -> super(2) toString: -> superString = super.toString() .replace(/an animal/, 'a monkey') "#{superString} and #{@numberOfLegs} bananas"
Premières impressions
La première chose que vous remarquerez peut-être est que ES6 est encore beaucoup plus verbeux que CoffeeScript. Un raccourci très pratique dans CoffeeScript est la prise en charge de l'affectation automatique des variables d'instance dans le constructeur :
constructor: (@numberOfLegs) ->
Cela équivaut à ce qui suit dans ES6 :
constructor(numberOfLegs) { this.numberOfLegs = numberOfLegs }
Bien sûr, ce n'est pas vraiment la fin du monde, et cette syntaxe plus explicite est plus facile à lire. Une autre petite chose qui manque est que nous n'avons pas de raccourci @
pour this
, ce qui était toujours agréable venant d'un arrière-plan Ruby, mais encore une fois, ce n'est pas un gros problème. En général, nous nous sentons plutôt chez nous ici, et je préfère en fait la syntaxe ES6 pour définir les méthodes.
Comment Super !
Il y a un petit hic avec la façon dont le super
est géré dans ES6. CoffeeScript gère le super
à la manière de Ruby ("veuillez envoyer un message à la méthode de la superclasse du même nom"), mais la seule fois où vous pouvez utiliser un super "nu" dans ES6, c'est dans le constructeur. ES6 suit une approche plus conventionnelle de type Java où super
est une référence à l'instance de la superclasse.
Je reviendrai
Dans CoffeeScript, nous pouvons utiliser des retours implicites pour créer un beau code comme celui-ci :
foo = -> if Math.random() > 0.5 if Math.random() > 0.5 if Math.random() > 0.5 "foo"
Ici, vous avez une très petite chance de récupérer "foo" lorsque vous appelez foo()
. ES6 n'a rien de tout cela et nous oblige à revenir en utilisant le mot-clé return
:
const foo = () => { if (Math.random() > 0.5 ) { if (Math.random() > 0.5 ) { if (Math.random() > 0.5 ) { return "foo" } } } }
Comme nous pouvons le voir dans l'exemple ci-dessus, c'est généralement une bonne chose, mais je me retrouve toujours à oublier de l'ajouter et à obtenir des retours indéfinis inattendus partout ! Il y a une exception que j'ai rencontrée, concernant les fonctions fléchées, où vous obtenez un retour implicite pour une doublure comme celle-ci :
array.map( x => x * 10 )
C'est plutôt pratique mais peut être un peu déroutant car vous avez besoin du return
si vous ajoutez les accolades :
array.map( x => { return x * 10 })
Cependant, cela a toujours du sens. Le raisonnement étant que si vous avez ajouté les accolades, c'est parce que vous voulez utiliser plusieurs lignes, et si vous avez plusieurs lignes, il est logique d'être clair sur ce que vous retournez.
Bonus de syntaxe de classe ES6
Nous avons vu que CoffeeScript a quelques astuces syntaxiques dans sa manche lorsqu'il s'agit de définir des classes, mais ES6 a aussi quelques astuces qui lui sont propres.
Getters et Setters
ES6 a un support puissant pour l'encapsulation via des getters et des setters, comme illustré dans l'exemple suivant :
class BananaStore { constructor() { this._bananas = [] } populate() { // fetch our bananas from the backend here } get bananas() { return this._bananas.filter( banana => banana.isRipe ) } set bananas(bananas) { if (bananas.length > 100) { throw `Wow ${bananas.length} is a lot of bananas!` } this._bananas = bananas } }
Grâce au getter, si nous accédons à bananaStore.bananas
, il ne renverra que des bananes mûres. C'est vraiment génial car dans CoffeeScript, nous aurions besoin de l'implémenter via une méthode getter comme bananaStore.getBananas()
. Cela ne semble pas du tout naturel lorsqu'en JavaScript nous sommes habitués à accéder directement aux propriétés. Cela rend également le développement déroutant car nous devons penser pour chaque propriété comment y accéder - est-ce .bananas
ou getBananas()
?

Le setter est tout aussi utile car nous pouvons nettoyer l'ensemble de données ou même lancer une exception lorsque des données invalides sont définies, comme indiqué dans l'exemple de jouet ci-dessus. Cela sera très familier à toute personne ayant une formation Ruby.
Personnellement, je ne pense pas que cela signifie que vous devriez devenir fou en utilisant des getters et des setters pour tout. Si vous pouvez gagner quelque chose en encapsulant vos variables d'instance via des getters et des setters (comme dans l'exemple ci-dessus), alors allez-y et faites-le. Mais imaginez que vous ayez juste un drapeau booléen tel que isEditable
. Si vous ne perdez rien en exposant directement la propriété isEditable
, je dirais que c'est la meilleure approche, car c'est la plus simple.
Méthodes statiques
ES6 prend en charge les méthodes statiques, ce qui nous permet de faire cette astuce :
class Foo { static new() { return new this } }
Maintenant, si vous détestez écrire new Foo()
, vous pouvez maintenant écrire Foo.new()
. Les puristes grogneront car new
est un mot réservé, mais après un test très rapide, il semble fonctionner correctement dans Node.js et avec les navigateurs modernes. Mais vous ne voulez probablement pas l'utiliser en production !
Malheureusement, comme il n'y a pas de prise en charge des propriétés statiques dans ES6, si vous souhaitez définir des constantes de classe et y accéder dans votre méthode statique, vous devrez faire quelque chose comme ceci, ce qui est un peu hackish :
class Foo { static imgPath() { return `${this.ROOT_PATH}/img` } } Foo.ROOT_PATH = '/foo'
Il existe une proposition ES7 pour implémenter des propriétés d'instance et de classe déclaratives, afin que nous puissions faire quelque chose comme ceci, ce qui serait bien :
class Foo { static ROOT_PATH = '/foo' static imgPath() { return `${this.ROOT_PATH}/img` } }
Cela peut déjà être fait assez élégamment dans CoffeeScript :
class Foo @ROOT_PATH: '/foo' @imgPath: -> @ROOT_PATH
Interpolation de chaîne
Le moment est venu où nous pouvons enfin faire de l'interpolation de chaîne en Javascript !
`I am so happy that in the year ${new Date().getFullYear()} we can interpolate strings`
Venant de CoffeeScript/Ruby, cette syntaxe semble un peu beurk. Peut-être à cause de mon arrière-plan Ruby où un backtick est utilisé pour exécuter des commandes système, cela semble plutôt faux au début. Utiliser aussi le signe du dollar donne l'impression d'être dans les années 80 - mais peut-être que c'est juste moi.
Je suppose qu'en raison de problèmes de compatibilité descendante, il n'était tout simplement pas possible d'implémenter l'interpolation de chaîne de style CoffeeScript. "What a #{expletive} shame"
.
Fonctions fléchées
Les fonctions fléchées sont prises pour acquises dans CoffeeScripter. ES6 a pratiquement implémenté la syntaxe des flèches grasses de CoffeeScript. Nous obtenons donc une belle syntaxe courte, et nous obtenons la portée lexicale this
, nous n'avons donc pas à sauter dans des cerceaux comme celui-ci lors de l'utilisation d'ES5 :
var that = this doSomethingAsync().then( function(res) { that.foo(res) })
L'équivalent dans ES6 est :
doSomethingAsync().then( res => { this.foo(res) })
Remarquez comment j'ai omis la parenthèse autour du rappel unaire, parce que je le peux !
Littéraux d'objets sur les stéroïdes
Les littéraux d'objet dans ES6 ont de sérieuses améliorations de performances. Ils peuvent faire toutes sortes de choses de nos jours !
Noms de propriété dynamiques
Je n'avais pas réalisé avant d'écrire cet article que depuis CoffeeScript 1.9.1 nous pouvons maintenant faire ceci :
dynamicProperty = 'foo' obj = {"#{dynamicProperty}": 'bar'}
Ce qui est beaucoup moins pénible que quelque chose comme ça qui était nécessaire auparavant :
dynamicProperty = 'foo' obj = {} obj[dynamicProperty] = 'bar'
ES6 a une syntaxe alternative qui, je pense, est plutôt sympa, mais pas une énorme victoire :
let dynamicProperty = 'foo' let obj = { [dynamicProperty]: 'bar' }
Raccourcis funky
Ceci est en fait tiré de CoffeeScript, mais c'était quelque chose que j'ignorais jusqu'à présent. C'est très utile en tout cas :
let foo = 'foo' let bar = 'bar' let obj = { foo, bar }
Maintenant, le contenu de obj est { foo: 'foo', bar: 'bar' }
. C'est super utile et mérite d'être rappelé.
Tout ce qu'une classe peut faire, je peux le faire aussi !
Les littéraux d'objet peuvent désormais faire à peu près tout ce qu'une classe peut faire, même quelque chose comme :
let obj = { _foo: 'foo', get foo() { return this._foo }, set foo(str) { this._foo = str }, isFoo() { return this.foo === 'foo' } }
Je ne sais pas trop pourquoi vous voudriez commencer à le faire, mais bon, maintenant vous le pouvez ! Vous ne pouvez pas définir de méthodes statiques bien sûr… car cela n'aurait aucun sens.
Boucles pour vous embrouiller
La syntaxe de la boucle for ES6 va introduire de jolis bugs de mémoire musculaire pour les CoffeeScripters expérimentés, car le for-of de ES6 se traduit par for-in de CoffeeSCript, et vice-versa.
ES6
for (let i of [1, 2, 3]) { console.log(i) } // 1 // 2 // 3
CoffeeScript
for i of [1, 2, 3] console.log(i) # 0 # 1 # 2
Dois-je passer à ES6 ?
Je travaille actuellement sur un projet Node.js assez important en utilisant 100% CoffeeScript, et j'en suis toujours très heureux et super productif. Je dirais que la seule chose dont je suis vraiment jaloux dans ES6, ce sont les getters et les setters.
De plus, il est encore légèrement douloureux d'utiliser ES6 dans la pratique aujourd'hui. Si vous êtes en mesure d'utiliser la toute dernière version de Node.js, vous obtenez presque toutes les fonctionnalités d'ES6, mais pour les anciennes versions et dans le navigateur, les choses sont encore moins roses. Oui, vous pouvez utiliser Babel, mais cela signifie bien sûr intégrer Babel dans votre pile.
Cela dit, je peux voir ES6 au cours de la prochaine année ou deux gagner beaucoup de terrain, et j'espère voir des choses encore plus grandes dans ES7.
Ce dont je suis vraiment satisfait avec ES6, c'est que le « bon vieux JavaScript » est presque aussi convivial et puissant prêt à l'emploi que CoffeeScript. C'est formidable pour moi en tant que pigiste - dans le passé, j'étais un peu opposé à travailler sur des projets JavaScript - mais avec ES6, tout semble un peu plus brillant.