Was ist neu in ES6? Perspektive eines CoffeeScript Convert

Veröffentlicht: 2022-03-11

Ich bin jetzt seit über zwei Jahren ein CoffeeScript-Fan. Ich finde, dass ich beim Schreiben von CoffeeScript produktiver bin, weniger dumme Fehler mache und mit Unterstützung für Quellkarten das Debuggen von CoffeeScript-Code völlig problemlos ist.

Was ist neu in ES6? Perspektive eines CoffeeScript Convert

Kürzlich habe ich mit Babel mit ES6 gespielt, und insgesamt bin ich ein Fan. In diesem Artikel werde ich meine Erkenntnisse zu ES6 aus der Perspektive eines CoffeeScript-Konvertierers zusammenstellen, mir die Dinge ansehen, die ich liebe, und sehen, was mir noch fehlt.

Syntaktisch signifikante Einrückung: Segen oder Fluch?

Ich hatte schon immer eine gewisse Hassliebe zu den syntaktisch signifikanten Einrückungen von CoffeeScript. Wenn alles gut geht, kommt das „Schau ma, keine Hände!“ prahlen mit der Verwendung einer solchen super-minimalistischen Syntax. Aber wenn etwas schief geht und eine falsche Einrückung echte Fehler verursacht (vertrauen Sie mir, das passiert), hat es das Gefühl, dass sich die Vorteile nicht lohnen.

 if iWriteCoffeeScript if iAmNotCareful badThings = 'happen'

Normalerweise werden diese Fehler verursacht, wenn Sie einen Codeblock mit einigen verschachtelten Anweisungen haben und versehentlich eine Anweisung falsch einrücken. Ja, es wird normalerweise durch müde Augen verursacht, aber meiner Meinung nach passiert es viel wahrscheinlicher in CoffeeScript.

Als ich anfing, ES6 zu schreiben, war ich mir nicht ganz sicher, was mein Bauchgefühl sein würde. Es stellte sich heraus, dass es sich wirklich gut anfühlte, wieder mit geschweiften Klammern zu beginnen. Auch die Verwendung eines modernen Editors hilft, da Sie in der Regel nur die geschweiften Klammern öffnen müssen und Ihr Editor diese freundlicherweise für Sie schließt. Es fühlte sich ein bisschen an, als würde man nach der Voodoo-Magie von CoffeeScript in ein ruhiges, klares Universum zurückkehren.

 if (iWriteES6) { if (iWriteNestedStatements) { let badThings = 'are less likely to happen' } }

Natürlich bestehe ich darauf, die Semikolons wegzulassen. Wenn wir sie nicht brauchen, sage ich, wirf sie weg. Ich finde sie hässlich und es ist zusätzliches Tippen.

Klassenunterstützung

Die Klassenunterstützung in ES6 ist fantastisch, und wenn Sie von CoffeeScript umsteigen, werden Sie sich wie zu Hause fühlen. Vergleichen wir die Syntax zwischen den beiden mit einem einfachen Beispiel:

ES6-Klasse

 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` } }

CoffeeScript-Klasse

 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"

Erste Eindrücke

Das erste, was Ihnen vielleicht auffällt, ist, dass ES6 immer noch viel ausführlicher ist als CoffeeScript. Eine sehr praktische Abkürzung in CoffeeScript ist die Unterstützung für die automatische Zuweisung von Instanzvariablen im Konstruktor:

 constructor: (@numberOfLegs) ->

Dies entspricht dem Folgenden in ES6:

 constructor(numberOfLegs) { this.numberOfLegs = numberOfLegs }

Natürlich ist das nicht wirklich das Ende der Welt, und wenn überhaupt, ist diese explizitere Syntax einfacher zu lesen. Eine weitere kleine Sache, die fehlt, ist, dass wir dafür keine @ -Verknüpfung this , was sich immer gut anfühlte, wenn es von einem Ruby-Hintergrund kam, aber auch hier kein massiver Deal-Breaker ist. Im Allgemeinen fühlen wir uns hier ziemlich zu Hause, und ich bevorzuge tatsächlich die ES6-Syntax zum Definieren von Methoden.

Wie super!

Es gibt einen kleinen Fallstrick bei der Art und Weise, wie super in ES6 gehandhabt wird. CoffeeScript handhabt super auf Ruby-Art („Bitte senden Sie eine Nachricht an die gleichnamige Methode der Oberklasse“), aber das einzige Mal, dass Sie ein „nacktes“ Super in ES6 verwenden können, ist der Konstruktor. ES6 folgt einem konventionelleren Java-ähnlichen Ansatz, bei dem super ein Verweis auf die Instanz der Superklasse ist.

Ich komme wieder

In CoffeeScript können wir implizite Rückgaben verwenden, um schönen Code wie den folgenden zu erstellen:

 foo = -> if Math.random() > 0.5 if Math.random() > 0.5 if Math.random() > 0.5 "foo"

Hier haben Sie eine sehr geringe Chance, „foo“ zurückzubekommen, wenn Sie foo() aufrufen. ES6 hat nichts davon und zwingt uns, mit dem Schlüsselwort return :

 const foo = () => { if (Math.random() > 0.5 ) { if (Math.random() > 0.5 ) { if (Math.random() > 0.5 ) { return "foo" } } } }

Wie wir im obigen Beispiel sehen können, ist dies im Allgemeinen eine gute Sache, aber ich vergesse immer noch, es hinzuzufügen, und erhalte überall unerwartete undefinierte Rückgaben! Es gibt eine Ausnahme, auf die ich in Bezug auf Pfeilfunktionen gestoßen bin, bei der Sie eine implizite Rückgabe für Einzeiler wie diese erhalten:

 array.map( x => x * 10 )

Das ist ziemlich praktisch, kann aber etwas verwirrend sein, da Sie die return benötigen, wenn Sie die geschweiften Klammern hinzufügen:

 array.map( x => { return x * 10 })

Es macht aber trotzdem Sinn. Der Grund dafür ist, dass Sie, wenn Sie die geschweiften Klammern hinzugefügt haben, mehrere Zeilen verwenden möchten, und wenn Sie mehrere Zeilen haben, ist es sinnvoll, klar zu machen, was Sie zurückgeben.

ES6-Klassensyntax-Boni

Wir haben gesehen, dass CoffeeScript ein paar syntaktische Tricks im Ärmel hat, wenn es um die Definition von Klassen geht, aber ES6 hat auch ein paar eigene Tricks.

Getter und Setter

ES6 bietet leistungsstarke Unterstützung für die Kapselung über Getter und Setter, wie im folgenden Beispiel veranschaulicht:

 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 } }

Dank des Getters werden beim Zugriff auf bananaStore.bananas nur reife Bananen zurückgegeben. Das ist wirklich großartig, da wir dies in CoffeeScript über eine Getter-Methode wie bananaStore.getBananas() implementieren müssten. Dies fühlt sich überhaupt nicht natürlich an, wenn wir in JavaScript daran gewöhnt sind, direkt auf Eigenschaften zuzugreifen. Es macht die Entwicklung auch verwirrend, weil wir für jede Eigenschaft überlegen müssen, wie wir darauf zugreifen sollen - ist es .bananas oder getBananas() ?

Der Setter ist ebenso nützlich, da wir den Datensatz bereinigen oder sogar eine Ausnahme auslösen können, wenn ungültige Daten festgelegt werden, wie im obigen Spielzeugbeispiel gezeigt. Dies wird jedem mit einem Ruby-Hintergrund sehr vertraut sein.

Ich persönlich denke nicht, dass Sie verrückt werden sollten, wenn Sie Getter und Setter für alles verwenden. Wenn Sie etwas erreichen können, indem Sie Ihre Instanzvariablen über Getter und Setter kapseln (wie im obigen Beispiel), dann tun Sie dies. Aber stellen Sie sich vor, Sie haben nur ein boolesches Flag wie isEditable . Wenn Sie nichts verlieren würden, indem Sie die isEditable Eigenschaft direkt verfügbar machen, würde ich argumentieren, dass dies der beste Ansatz ist, da es der einfachste ist.

Statische Methoden

ES6 unterstützt statische Methoden, was uns diesen ungezogenen Trick ermöglicht:

 class Foo { static new() { return new this } }

Wenn Sie es hassen, new Foo() zu schreiben, können Sie jetzt Foo.new() schreiben. Puristen werden murren, da new ein reserviertes Wort ist, aber nach einem sehr kurzen Test scheint es in Node.js und mit modernen Browsern gut zu funktionieren. Aber Sie möchten dies wahrscheinlich nicht in der Produktion verwenden!

Da es in ES6 leider keine Unterstützung für statische Eigenschaften gibt, müssen Sie, wenn Sie Klassenkonstanten definieren und in Ihrer statischen Methode darauf zugreifen möchten, so etwas tun, was ein bisschen hackisch ist:

 class Foo { static imgPath() { return `${this.ROOT_PATH}/img` } } Foo.ROOT_PATH = '/foo'

Es gibt einen ES7-Vorschlag, deklarative Instanz- und Klasseneigenschaften zu implementieren, damit wir so etwas tun können, was schön wäre:

 class Foo { static ROOT_PATH = '/foo' static imgPath() { return `${this.ROOT_PATH}/img` } }

Das geht schon recht elegant in CoffeeScript:

 class Foo @ROOT_PATH: '/foo' @imgPath: -> @ROOT_PATH

String-Interpolation

Die Zeit ist gekommen, in der wir endlich String-Interpolation in Javascript durchführen können!

 `I am so happy that in the year ${new Date().getFullYear()} we can interpolate strings`

Diese Syntax kommt von CoffeeScript/Ruby und fühlt sich ein bisschen eklig an. Vielleicht wegen meines Ruby-Hintergrunds, wo ein Backtick verwendet wird, um Systembefehle auszuführen, fühlt es sich zunächst ziemlich falsch an. Auch die Verwendung des Dollarzeichens fühlt sich irgendwie nach Achtzigern an – aber vielleicht liegt das an mir.

Ich nehme an, aufgrund von Bedenken hinsichtlich der Abwärtskompatibilität war es einfach nicht möglich, die Zeichenfolgeninterpolation im CoffeeScript-Stil zu implementieren. "What a #{expletive} shame" .

Pfeilfunktionen

Pfeilfunktionen sind in CoffeeScripter selbstverständlich. ES6 hat ziemlich genau die fette Pfeilsyntax von CoffeeScript implementiert. Wir erhalten also eine schöne kurze Syntax und das lexikalisch begrenzte this , sodass wir bei der Verwendung von ES5 nicht so durch Reifen springen müssen:

 var that = this doSomethingAsync().then( function(res) { that.foo(res) })

Das Äquivalent in ES6 ist:

 doSomethingAsync().then( res => { this.foo(res) })

Beachten Sie, wie ich die Klammer um den unären Rückruf weggelassen habe, weil ich es kann!

Objektliterale auf Steroiden

Objektliterale in ES6 weisen einige ernsthafte Leistungsverbesserungen auf. Die können heutzutage alles Mögliche!

Dynamische Eigenschaftsnamen

Bis zum Schreiben dieses Artikels war mir nicht klar, dass wir dies seit CoffeeScript 1.9.1 jetzt tun können:

 dynamicProperty = 'foo' obj = {"#{dynamicProperty}": 'bar'}

Was viel weniger schmerzt als so etwas, das vorher notwendig war:

 dynamicProperty = 'foo' obj = {} obj[dynamicProperty] = 'bar'

ES6 hat eine alternative Syntax, die meiner Meinung nach ziemlich nett ist, aber kein großer Gewinn:

 let dynamicProperty = 'foo' let obj = { [dynamicProperty]: 'bar' }

Funkige Verknüpfungen

Das ist eigentlich von CoffeeScript übernommen, aber das war etwas, was ich bis jetzt nicht wusste. Es ist auf jeden Fall sehr nützlich:

 let foo = 'foo' let bar = 'bar' let obj = { foo, bar }

Jetzt ist der Inhalt von obj { foo: 'foo', bar: 'bar' } . Das ist super nützlich und es lohnt sich, sich daran zu erinnern.

Alles, was eine Klasse kann, kann ich auch!

Objektliterale können jetzt so ziemlich alles tun, was eine Klasse tun kann, sogar so etwas wie:

 let obj = { _foo: 'foo', get foo() { return this._foo }, set foo(str) { this._foo = str }, isFoo() { return this.foo === 'foo' } }

Ich bin mir nicht ganz sicher, warum du damit anfangen willst, aber hey, jetzt kannst du es! Sie können natürlich keine statischen Methoden definieren … das würde überhaupt keinen Sinn machen.

For-Schleifen, um Sie zu verwirren

Die for-Loop-Syntax von ES6 führt erfahrene CoffeeScripter zu einigen netten Fehlern im Muskelgedächtnis, da das for-of von ES6 in for-in von CoffeeSCript übersetzt wird und umgekehrt.

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

Soll ich zu ES6 wechseln?

Ich arbeite derzeit an einem ziemlich großen Node.js-Projekt mit 100% CoffeeScript und bin immer noch sehr glücklich und super produktiv damit. Ich würde sagen, dass das Einzige, worauf ich in ES6 wirklich neidisch bin, die Getter und Setter sind.

Auch heute noch ist es in der Praxis mit ES6 etwas schmerzhaft. Wenn Sie die allerneueste Node.js-Version verwenden können, erhalten Sie fast alle ES6-Funktionen, aber für ältere Versionen und im Browser sieht es noch weniger rosig aus. Ja, Sie können Babel verwenden, aber das bedeutet natürlich, Babel in Ihren Stack zu integrieren.

Allerdings kann ich sehen, dass ES6 in den nächsten ein oder zwei Jahren viel an Boden gewinnt, und ich hoffe, noch größere Dinge in ES7 zu sehen.

Worüber ich bei ES6 wirklich erfreut bin, ist, dass „einfaches altes JavaScript“ von Haus aus fast so benutzerfreundlich und leistungsstark ist wie CoffeeScript. Für mich als Freelancer ist das super – früher war ich etwas abgeneigt, an JavaScript-Projekten zu arbeiten – aber mit ES6 wirkt alles noch ein bisschen glänzender.