By Robert Dickert

ES2015 is here with Meteor 1.2, and now’s the time to get started. ES2015 is the best version of JavaScript yet, and you can use it to write higher quality and more readable code. It was simple to add it to my project with meteor add ecmascript . Once that was done, though, it was hard to know where to start, so I talked to the Meteor core devs to find out what I needed to know. Here’s what I learned.

Meteor worries about backwards compatibility, so you don’t have to.

One of the main problems with using ES2015 is that no existing platform (even Node or the latest release of a modern browser like Chrome, Firefox, or Edge) supports all of ES2015. Selecting which features to use and making sure they can be transpiled back to other platforms can be an intimidating task. Meteor takes the worry out of using ES2015 by curating the features that can be reliably and performantly transpiled to work on older browsers. Meteor uses Babel to transpile your code and adds a few polyfills so you can use any supported ECMAscript feature without worrying about it. It even works on IE8. Source maps are built-in, so even when debugging, it will feel like you are working directly in ES2015. If you are troubleshooting an old browser without source maps, you still get line numbers to help you pinpoint source code issues. As these features become mature, your code will work natively without changes.

It’s not all or nothing.

I was asked by a knowledgable programmer how Meteor divides the code for ES2015 and regular ES5. He was surprised to learn that Meteor is not using a special file extension. Many people don’t realize that ES2015 is fully backward-compatible with ES5 and below, so you don’t need to segregate your code. You can feel free to add ES2015 features little by little as you learn them and become comfortable. You can start with small changes, and work your way up to a new way of coding.

An embarrassment of riches

The main trouble with ES2015 is knowing where to get started. There are a large number of new features in ES2015. I asked Sashko Stubailo, a Meteor core developer, how he recommends starting the switch to ES2015, and he gave me a short list.

1: Start using const and let instead of var

This is one of the easiest to learn, and yet this one change lead to a significant improvement in code quality. const and let are block-scoped. A JavaScript block is the code encased in braces {} . Block scoping means that the variable you declare will only exist within the block – unlike a var, which is either global or function-scoped – so you won’t have to worry about loop variables accidentally clobbering a variable in the enclosing code. This prevents some hard-to-debug errors. For example:

for (var i = 1; i < 6; i++) { Meteor.setTimeout(function () { console.log(i); }, 0); //=> 6 6 6 6 6 -- probably not our intention!} console.log(i); //=>6 -- we have polluted the scope with i

If we happened to be inside another loop that uses a variable i , we would run into problems here. It’s better to isolate i to the code in the loop's block.

for (let i = 1; i < 6; i++) {

Meteor.setTimeout(function () { console.log(i); }, 0); //=> 1 2 3 4 5 -- kept i's value for each iteration}

console.log(i); //=> error/undefined -- safe

A couple of things to note: const fixes the name binding but does not freeze objects. If you declare a mutable object like an array or object, it is still mutable – but that object will always be associated with the name you declare. Also, be careful just changing var s in existing code. The differences in behavior may introduce bugs.

You can safely try const and let without much study, but when you are ready to learn the nuances, you can read more here.

2: Replace var self = this with arrow functions

There is an idiom used at Meteor and many other projects to declare var self = this in methods. This is a convention to solve the problem of context binding in JavaScript, where functions called from within a method by default our bound to the global scope instead of the object they were called from. For example, say you have a chat window where messages are dated using the moment library. You might have them automatically update:

Template.message.onCreated(function () {

this.now = new ReactiveVar(moment());

var self = this;

Meteor.setInterval(function () {

self.now.set(moment()); // `this` is the global scope, not the object's scope }, 30000); });

ES2015 gives us the arrow function ( () => ) to solve this in a compact syntax. this will always be bound to the object in which it was declared (lexical scope).

Template.message.onCreated(function () {

this.now = new ReactiveVar(moment()); Meteor.setInterval(() => { this.now.set(moment()); // bound: OK to use `this` }, 30000); });

This code removes an error-prone workaround to one of JavaScript’s “bad parts” and makes the code tighter. Note that you can’t replace the function keyword in onCreated , because that function relies on this being set to the template instance.

3: Replace string concatenations with template strings

Have you ever built a message with strings only to find it prints incorrectly?

const user = "James";

console.log("You are logged in as" + user + "."); // => oops, logs "You are logged in asJames."

Template strings will be familiar to users of many other popular languages. If you enclose the string in backticks (`), you can insert JavaScript expressions inside ${} :

const user = "James";

console.log(`You are logged in as ${user}.`); // => correct spacing: "You are logged in as James."

This is much more readable and less error-prone. An aside: Avital Oliver commented that this feature helped make writing shell scripts in JavaScript much better.

4: Use object methods

The new object method syntax can be used many places in Meteor. For example:

Template.body.helpers({ todos() { return Todos.find(); } });

This is a nice, readable syntax, and it creates named functions that will print their names correctly in stacktraces without using the longer, more awkward todos: function todos()... syntax.

5: Use object shorthand notation to return multiple values from functions

Another nice shorthand in ES2015 allows us to write {firstName: firstName, lastName: lastName} as {firstName, lastName} . In the function below, it gives us a good way to return multiple values.

function getPerson() { const firstName = "Joe"; const lastName = "Bloggs"; return {firstName, lastName};}const {firstName, lastName} = getPerson(); console.log(firstName, lastName); //=> Joe Bloggs

A nice quality of this is that we can add return values to the function without breaking the the code that calls it:

function getPerson() { const firstName = "Joe"; const lastName = "Bloggs"; const age = 32; return {firstName, lastName, age}; // <-- new return value}const {firstName, lastName} = getPerson(); // old code still worksconsole.log(firstName, lastName); const {age} = getPerson(); // new code can access ` age ` too.

6: Use native methods like [].forEach instead of underscore

One of the benefits of using a transpiled language is that you can use all of the features available. Meteor will handle making it work across all browsers. Some of the native methods that you may have been wary of using include:

Array.prototype.indexOf

Array.prototype.forEach

Array.prototype.map

Array.prototype.reduce

These are actually ES5 methods, but now you can rely on them, and Meteor will assure that they work on all browsers via the es5-shim package. As of Meteor 1.2, the recommended style is to assume that these methods exist.

7: Use destructuring and argument defaults to simplify your code

Destructuring is a feature that is pretty difficult to describe but pretty intuitive if you show it. Whereas literal syntax lets you build objects in an intuitive, readable way, there used to never be a simple way to access those inner properties without a fair amount of code. Destructuring allows you to parse objects in a way that uses less code and is more intuitive:

const person = {name: "Jean", _id: 123}const {name, _id} = person; console.log(name, _id); //=> "Jean" 123

Here we declared two const s, name and _id and assigned them values based on the structure of the person object. It resembles a sort of “reverse object literal,” it it can make your code simultaneously more compact and more readable. Let’s rename _id to id and add a default title to make this even more powerful:

const person = {name: "Jean", _id: 123}const {name, _id: id, title="core dev"} = person; console.log(name, id, title); //=> "Jean" 123 "core dev"

We added a title property with a default value. When that value was not provided, the default was assigned to title . This replaces lines like const title = person.title || 'core dev' .

This is a good pattern in general, but it really shines in function declarations:

const person = {name: "Jean", _id: 123}

function printPerson ({name, _id: id, title="core dev"}){ console.log(name, id, title);} printPerson(person) //=> "Jean" 123 "core dev

Going deeper

If you want see how far you can go with this, check out this demo by Ben Newman. It is the source of the gif at the top of this post, and it demonstrates how to use ES2015 to make the code both more concise and easier to understand.

All ahead full

I think you will find that ES2015 is very nice to work with, leading to cleaner and less error-prone code. Once you have a taste of what is possible, you may want to check out further resources on ES2015. ES2015 adds some deep features, and this is a great time to learn some great new techniques as you check them out. A couple of resources that may help:

I hope you enjoy your first steps with ES2015 as much as we have here at Meteor.