神刀安全网

What's New in ES6? Perspective of a CoffeeScript Convert

I’ve been a CoffeeScript fan for over two years now. I find I’m more productive writing CoffeeScript, make less silly mistakes, and with support for source maps debugging CoffeeScript code is completely painless.

What's New in ES6? Perspective of a CoffeeScript Convert

Recently I’ve been playing with ES6 using Babel , and overall I’m a fan. In this article, I will compile my findings on ES6 from the perspective of a CoffeeScript convert, look at the things I love and see what things I’m still missing.

Syntactically Significant Indentation: a Boon or a Bane?

I’ve always had a bit of a love/hate relationship with CoffeeScript’s syntactically significant indentation. When everything goes fine you get the “Look ma, no hands!” bragging rights of using such a super-minimalistic syntax. But when things go wrong and incorrect indentation causes real bugs (trust me, it happens), it doesn’t feel like the benefits are worthwhile.

 if iWriteCoffeeScript    if iAmNotCareful       badThings = 'happen' 

Typically these bugs are caused when you have a block of code with a few nested statements, and you accidentally indent one statement incorrectly. Yes it’s usually caused by tired eyes, but it’s much more likely to happen in CoffeeScript in my opinion.

What's New in ES6? Perspective of a CoffeeScript Convert

When I started writing ES6 I wasn’t quite sure what my gut reaction would be. It turns out it actually felt really nice to start using curly braces again. Using a modern editor also helps, as generally you only have to open the brace and your editor is kind enough to close it for you. It felt a bit like returning to a calm, clear universe after the voodoo magic of CoffeeScript.

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

Of course I insist on throwing out the semicolons. If we don’t need them, I say throw them out. I find them ugly and it’s extra typing.

Class Support

The class support in ES6 is fantastic, and if you’re moving from CoffeeScript you will feel right at home. Let’s compare the syntax between the two with a simple example:

ES6 Class

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

First Impressions

What's New in ES6? Perspective of a CoffeeScript Convert

The first thing that you may notice is that ES6 is still a lot more verbose than CoffeeScript. One shortcut that is very handy in CoffeeScript is the support for automatic assignment of instance variables in the constructor:

constructor: (@numberOfLegs) -> 

This is equivalent to the following in ES6:

 constructor(numberOfLegs) {     this.numberOfLegs = numberOfLegs  } 

Of course that’s not really the end of the world, and if anything this more explicit syntax is easier to read. Another small thing missing is that we have no @ shortcut for this , which always felt nice coming from a Ruby background, but again isn’t a massive deal-breaker. In general we feel pretty much at home here, and I actually prefer the ES6 syntax for defining methods.

How Super!

There is a small gotcha with the way super is handled in ES6. CoffeeScript handles super in the Ruby way (“please send a message to the superclass method of the same name”), but the only time you can use a “naked” super in ES6 is in the constructor. ES6 follows a more conventional Java-like approach where super is a reference to the superclass instance.

I Will Return

In CoffeeScript we can use implicit returns to build beautiful code like the following:

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

Here you have a very small chance of getting “foo” back when you call foo() . ES6 is having none of this and forces us to return using the return keyword:

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

As we can see in the above example, this is generally a good thing, but I still find myself forgetting to add it and getting unexpected undefined returns all over the place! There is one exception I’ve come across, concerning Arrow Functions, where you get an implicit return for one liners like this:

array.map( x => x * 10 ) 

This is kind of handy but can be slightly confusing as you need the return if you add the curly braces:

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

However, it still makes sense. The reasoning being that if you have added the curly braces it’s because you want to use multiple lines, and if you have multiple lines it makes sense to be clear what you are returning.

ES6 Class Syntax Bonuses

We’ve seen that CoffeeScript has a few syntactical tricks up its sleeve when it comes to defining classes, but ES6 also has a few tricks of its own.

Getters and Setters

ES6 has powerful support for encapsulation via getters and setters, as illustrated in the following example:

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

0

Thanks to the getter, if we access bananaStore.bananas it will only return ripe bananas. This is really great as in CoffeeScript we would need to implement this via a getter method like bananaStore.getBananas() . This doesn’t feel at all natural when in JavaScript we are used to accessing properties directly. It also makes development confusing because we need to think for each property how we should access it – is it .bananas or getBananas() ?

The setter is equally useful as we can clean up the data set or even throw an exception when invalid data is set, as shown in the toy example above. This will be very familiar to anyone from a Ruby background.

I personally don’t think this means you should go crazy using getters and setters for everything. If you can gain something by encapsulating your instance variables via getters and setters (like in the example above) then go ahead and do so. But imagine you just have a boolean flag such as isEditable . If you would not lose anything by directly exposing the isEditable property, I would argue that’s the best approach, as it’s the simplest.

Static methods

ES6 has support for static methods, which lets us do this naughty trick:

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

1

Now if you hate writing new Foo() you can now write Foo.new() . Purists will grumble since new is a reserved word, but after a very quick test it seems to work in Node.js and with modern browsers just fine. But you probably don’t want to use this in production!

Unfortunately because there is no support for static properties in ES6, if you want to define class constants and access them in your static method you’d have to do something like this, which is a bit hackish:

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

2

There is an ES7 Proposal to implement declarative instance and class properties, so that we can do something like this, which would be nice:

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

3

This can already be done quite elegantly in CoffeeScript:

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

4

String Interpolation

The time has come when we can finally do string interpolation in Javascript!

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

5

Coming from CoffeeScript/Ruby, this syntax feels a bit yuk. Perhaps because of my Ruby background where a backtick is used to execute system commands, it feels pretty wrong at first. Also using the dollar sign feels kind of eighties – but maybe that’s just me.

What's New in ES6? Perspective of a CoffeeScript Convert

I suppose due to backwards compatibility concerns it just wasn’t possible to implement CoffeeScript style string interpolation. "What a #{expletive} shame" .

Arrow Functions

Arrow functions are taken for granted in CoffeeScripter. ES6 pretty much implemented the fat arrow syntax of CoffeeScript. So we get a nice short syntax, and we get the lexically scoped this , so we don’t have to jump through hoops like this when using ES5:

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

6

The equivalent in ES6 is:

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

7

Notice how I left out the parenthesis around the unary callback, because I can!

Object Literals on Steroids

Object literals in ES6 have some serious performance enhancements. They can do all kinds of things these days!

Dynamic Property Names

I didn’t actually realise until writing this article that since CoffeeScript 1.9.1 we can now do this:

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

8

Which is much less of a pain than something like this which was necessary before:

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

9

ES6 has an alternative syntax which I think is pretty nice, but not a huge win:

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

0

Funky Shortcuts

This is actually taken from CoffeeScript, but it was something I was ignorant of until now. It’s very useful anyhow:

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

1

Now the contents of obj is { foo: 'foo', bar: 'bar' } . That’s super useful, and worth remembering.

Everything a Class Can Do I Can Do Too!

Object literals can now do pretty much everything a class can do, even something like:

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

2

Not quite sure why you would want to start doing that but hey, now you can! You can’t define static methods of course… as that would make no sense at all.

For-loops to Confuse You

The ES6 for-loop syntax is going to introduce some nice muscle memory bugs for experienced CoffeeScripters, as the for-of of ES6 translates to for-in of CoffeeSCript, and vice-versa.

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

3

CoffeeScript

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

4

Should I Switch to ES6?

I’ve currently been working on a fairly largeNode.js project using 100% CoffeeScript, and I’m still very happy and super productive with it. I would say that the only thing I am really jealous of in ES6 are the getters and setters.

Also it is still slightly painful using ES6 in practice today. If you are able to use the very latest Node.js version, then you get nearly all ES6 features , but for older versions and in the browser things are still less rosy. Yes, you can use Babel, but of course that means integrating Babel into your stack.

Having said that I can see ES6 over the next year or two gaining a lot of ground, and I hope to see even greater things in ES7.

What I’m really pleased about with ES6 is that “plain old JavaScript” is nearly as friendly and powerful out-of-the-box as CoffeeScript. This is great for me as afreelancer – in the past I used to be a little bit averse to working on JavaScript projects – but with ES6 everything just seems that little bit shinier.

About the author

William Coates, Portugal

member since September 6, 2015

HTML5 JavaScript Ruby Ruby on Rails

William is a highly skilled full-stack developer and entrepreneur with fifteen years of experience working with web technologies. He loves to keep up to date with the latest tech, and has a real passion for the industry. [click to continue…]

原文  http://www.toptal.com/javascript/whats-new-in-es6-perspective-coffeescript

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » What's New in ES6? Perspective of a CoffeeScript Convert

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
分享按钮