# Moving to ES6 from CoffeeScript I fell in love with CoffeeScript a couple of years ago. Javascript has always seemed something of an interesting curiosity to me and I was happy to see the meteoric rise of Node.js, but coming from a background of Python I really preferred a cleaner syntax. In any fast moving community it is inevitable that things will change, and so today we see a big shift toward ES6, the new version of Javascript. It incorporates a handful of the nicer features from CoffeeScript and is usable today through tools like [Babel](https://babeljs.io/). Here are some of my thoughts and issues on moving away from CoffeeScript in favor of ES6. While reading I suggest keeping open a tab to [Babel's learning ES6](http://babeljs.io/docs/learn-es6/) page. The examples there are great. ## Punctuation Holy punctuation, Batman! Say goodbye to your whitespace and hello to parenthesis, curly braces, and semicolons again. Even with the advanced ES6 syntax you'll find yourself writing a lot more punctuation. Accept it now. You'll see it in the examples below. The good news is that with a good linter the code will still be pretty. ## Linting I used to use CoffeeLint. Now I use [ESLint](http://eslint.org/) through [babel-eslint](https://github.com/babel/babel-eslint). Along with the [Airbnb ES6 style guide](https://github.com/airbnb/javascript) it's quite strict. Your best bet is to set your editor up to lint as you type or on save. [Atom's eslint plugin](https://atom.io/packages/linter-eslint) seems to work well and looks for a locally-installed/configured eslint per directory, which should find the `.eslintrc` which you can copy from the Airbnb link above. Sublime has a [similar plugin](https://github.com/roadhump/SublimeLinter-eslint). ## Transpiling Since support for ES6 is still sketchy at best, you'll probably be transpiling just like you did with CoffeeScript. Unlike with CoffeeScript, there are some caveats, however. 1. Some ES6 features are just not available, like Proxies. Maybe some day we'll get the equivalent of Python's `__getattr__` and Ruby's `method_missing`, but that day is not today. Them's the breaks. 2. Some of the more interesting ES6 features require a polyfill/runtime. Symbols, generators, and new types like `WeakMap` for example. Example `package.json` snippet: ```json { ... "scripts": { "lint": "eslint --ext .js,.es6 src", "precompile": "npm run lint", "compile": "babel --optional runtime src --out-dir lib", }, "dependencies": { "babel-runtime": "^5.3.3" }, "devDependencies": { "babel": "^5.3.3", ... } ... } ``` Don't make the mistake of depending on `babel` because that will download **many** more packages than you need. Depend on `babel-runtime`, devDepend on `babel` for the build. By the way, these polyfills may have [strange edge case behavior](https://github.com/zloirock/core-js#caveats-when-using-symbol-polyfill) in some environments. 3. Some features are disabled by default. Specifically, those in [TC39 Stage 0](http://babeljs.io/docs/usage/experimental/) like list comprehensions. This isn't Babel's fault - the specifications are not yet complete. Yes, we had these in CoffeeScript and now have to wait to use them again. ## Let and Const Haven't you heard the news? `var` is out while her buddies `let` and `const` are in. With Javascript you should declare your variables. Default to `const` and if a value needs to change, then use `let`. The transpiler will enforce constants while the linter will complain if you use `var` or nothing. Keep in mind that `const` applies **only** to the value itself. This takes a little getting used to: ```js const name = 'Daniel'; // This is a compile error name = 'Kari'; // --------- const options = {}; const items = []; // This is *not* a compile error options.foo = 'bar'; options.baz = 5; items.push(1); items.push(2); // This *is* a compile error options = {}; items = null; ``` ## String Interpolation Luckly, string interpolation is one area where you just need to retrain your fingers: ```js const name = 'World'; console.log(`Hello, ${name}!`); ``` Notice the backticks instead of single quotes. It takes some time, so make sure your editor syntax highlights these properly or I guarantee you'll get stuff like `#{name}` printed out by accident. ## Functions Javascript has some new function types! With ES6 you can use both fat arrows and generators. Fat arrows behave like they do in CoffeeScript and keep `this` passed through to the function body. They support both an implicit return shorthand and a long form that has no implied return value. Splats are also supported, but the ellipsis is on the other side. Default arguments are also supported and work as you'd expect. Same with assignment destructuring. CoffeeScript: ```coffee square = (value) -> value * value someTask (err, result) => # Handle err and result myFunc = ({source, flag}, args...) -> otherFunc source, args... ``` Javascript: ```js const square = value => value * value; someTask((err, result) => { // Handle err and result }); function myFunc({source, flag}, ...args) { return otherFunc(source, ...args); } ``` ### Generators Generators provide a way to iterate through a large or possibly infinite list of items by only processing one at a time. [Read up on them](http://babeljs.io/docs/learn-es6/#generators) if you like. Here's a quick example: ```js // Instead of creating a 10000 item array, we yield each item as // needed. function *squares() { for (let n = 0; n < 10000; n++) { yield n * n; } } for (let square of squares()) { console.log(square); } ``` The `function *` syntax tells Babel that this is a generator, unlike in CoffeeScript where the presence of a `yield` statement made it a generator. Note that generators can both yield *and* return values. ## Classes I'm happy to report that classes are actually very similar, and my main gripe with them is something that's being worked on in the spec and also has a usable workaround (only being able to create functions in a class). The following example will show just how similar the syntaxes are, including things like inheritance. CoffeeScript: ```coffee class Account extends Foo @types = ['checking', 'savings'] constructor: (@balance) -> history: (done) -> someLongTask (err, data) -> # Do something with data done null, data deposit: (amount) -> @balance += amount ``` Javascript: ```js class Account extends Foo { constructor(balance) { this.balance = balance; } history(done) { someLongTask((err, data) => { // Do something with data done(null, data); }); } deposit(amount) { this.balance += amount; return this.balance; } } // Currently, you can only declare functions in a class Account.types = ['checking', 'savings']; ``` One cool feature is the ability to define `getters` and `setters`. Unfortunately they cannot be generator functions. ```js class Account { constructor() { this.balance = 0; } get underBudget() { return this.balance >= 0; } get overBudget() { return this.balance < 0; } } const myAccount = Account(); myAccount.balance = 100; console.log(myAccount.underBudget); // => true ``` ### Iterable Classes Another neat feature is the ability to make iterable classes, and the ability to use generators for the iterator. ```js class MyIterable { constructor(items) { this.items = items; } *[Symbol.iterator]() { for (let item of this.items) { yield `Hello, ${item}`; } } } const test = new MyIterable([1, 2, 3, 4, 5]); for (let item of test) { console.log(item); // => Hello, 1... } ``` ## Modules ES6 brings with it a [new module syntax](http://www.2ality.com/2014/09/es6-modules-final.html). It'll take some getting used to, because it includes both a default export and named exports. ```js import _ from 'lodash'; import {item1, item2} from './mylib'; import * as library from 'library'; export const name = 'Daniel'; export function abc() { return 1; } export class Toaster { // ... } export default function def() { return new Toaster(); } ``` Some interesting caveats: 1. If no default is given then you don't just get an object of all the named exports. Instead use `import * as moduleName from 'moduleName';`. Feels weird, right? Particularly for Python users, where `import *` is a four letter word. ```js // mymodule.js // ----------- export function yes() { return true; } // script-broken.js // ---------------- import mymodule from './mymodule'; // This gives an error about `undefined`! console.log(mymodule.yes()); // script-working.js // ----------------- import * as mymodule from './mymodule'; console.log(mymodule.yes()); ``` 2. If there is only one export and it is a default export, then `module.exports` will be set to it. If however there are other exports, you get something that's very strange to consume from normal Node.js `require` statements. ```js // mymodule.js // ----------- export function yes() { return true; } function no() { return false; } export default {yes, no}; // script-working.js // ----------------- import mymodule, {yes} from './mymodule'; console.log(mymodule.no()); console.log(yes()); // script-broken.js // ---------------- const mymodule = require('./mymodule'); // Wat? This is an error. console.log(mymodule.no()); // This works instead. Turns out the module is an object with a 'default' // key that contains the default export. console.log(mymodule.default.no()); ``` I currently don't have a great solution for this nonsense, but if you want to write libraries that are consumed by Node.js users who will likely be using `require`, this is something to keep in mind. As a possible workaround you **can** export default one thing and then assign all the things to it as properties. ## Conclusion Hopefully this article helps someone. It's been really fun learning ES6 and getting to play with all of the new toys like Babel and ESLint.