How's your ES6, ES2016, and ES2017?

Learn enough modern JavaScript to get by with this interactive cheatsheet.

Oh man, I love JavaScript, but you show me this stuff and it just doesn't look like anything I'm used to.

Robert Crocker, d3 meetup organizer

I love what you're teaching us, but I just can't wrap my head around all this weird syntax we're using.

Tamara, React+d3 workshop attendee

Sound like you? Keep scrolling.

This is an interactive ES6, ES2016, and ES2017 cheatsheet. It uses a series of small examples to show you the important parts of modern JavaScript. All code is editable and runnable so you can play with it all you want.

Many of my readers and workshop attendees complained that modern JavaScript looks weird at first. They could understand what I was saying, but couldn't read the code. So I built this cheatsheet to fix that.

In 5 minutes you will be able to read and understand modern JavaScript code. Using it on your own might take longer, but you can do it! :)

How to use this ES6, ES2016, and ES2017 cheatsheet

Welcome to the interactive ES6+ Cheatsheet. It's organized into 13 sections and 47 code samples. Each section has two columns: On the left, there's the old way of doing things. It uses the JavaScript you already know. On the right, there's the new way of doing things. It uses ES6, ES2016, and ES2017 to show you how to get the same result with modern syntax.

Code samples have two tabs. The first shows you the code, the second shows you what happens when you run it. You can and should edit the code. It runs straight away :)

This modern JavaScript cheatsheet is not an exhaustive list of new features. It's meant to show you the features you're most likely to run across in the wild. The ones you can use to make your code better.

Table of Contents

The full cheatsheet uses lazy loading to show 47 Codepen iframes. You might see empty spaces while they load.

PS: this website works better with JavaScript enabled

Variable Declarations

One of the biggest sources of bugs and confusion in JavaScript has been variable scoping. Without var everything was global, with var it was better but still weird. ES6 brings us let and const with proper lexical scoping, which makes it easier to understand where a variable is and isn't defined.

Constants follow the same scoping rules, but their value cannot be reassigned. const protects you against accidentally changing variables. It's good to use it for everything by default.

Careful, though. Constants are tricky with arrays and objects. The reference becomes constant, but the value does not.

One of my favorite features present in most modern JavaScript codebases is omitting repetitive object keys. I don't think this is quite in the standard yet, but it works in most React-based codebases via Babel. I include it because it's commonly used in the wild.

String Templates and Padding

String templates are one of my favorite ES6 features. They're amazing! They make it easy to put variables into strings, which often comes handy. Just wrap something in backticks ` and you can insert any JavaScript code with ${...}. Just don't get carried away.

In ES2017 we got string padding. I personally don't use it, but if you remember the leftpad controversy ... well, it seems a lot of people needed this 😅

You can use padStart to insert padding at the start of your string and padEnd to insert them at the end. We use start and end to support languages with different reading directions.

Destructuring

Destructuring is another magnificent feature. It gives you a convenient syntax to extract values from objects and arrays. Using it feels kind of like drawing the shape of your object, and JavaScript filling-in the blanks.

Spread Operator

You can think of the spread operator, ... as a part of destructuring. It's a convenient way to get "everything else that remains" from an Array. The object spread operatoro is also a great alternative to Object.assign or _.extend when making objects.

Object spread is still a stage 3 proposal, but looks like a sure bet to make it into the standard soon. Many browsers support it and it works by default in most React-based codebases via Babel. I include it here because it's commonly used in the wild.

Arrow Functions

Now this, this is the workhorse of modern JavaScript! The trusty arrow function. The thing that got everyone so excited when ES6 first started shaping up.

So what's the deal? this has historically been one of the trickiest parts of using JavaScript. Because of dynamic scoping you could never quite tell what this would point to in a function. It points to "current scope", which is different than the scope in which you defined your function. To many (most?) this is confusing.

Several more or less elegant ways emerged to deal with the confusion. Using .bind() is perhaps the best approach. But with arrow functions, you don't need any of that. An arrow function is always bound to the scope it's defined in. That means you can pass it around and its this variable will always point the same.

Arrow functions also use cleaner syntax, which makes them nicer to use as iterators. Which just so happens to be the most common reason you need binding.

Function Parameters

Function parameters in ES6 are sort of a special case of destructuring. All that icky stuff we used to do to achieve named params, default params, and optional params? No need. ES6 has you covered.

A common pattern for named params has been to pass around an object. If you can't remember which keys a function expects, tough.

The spread operator in params is especially useful when you need something like (a, b, ...everythingElse).

Behind the scenes, named params are just an object that's passed around. You can keep your old function calls, and just change the functions.

Classes

I am personally both a fan of classes and think they were the wrong step for JavaScript to take. Yes, they make object oriented programming (OOP) a whole lot nicer, which is good, but they also encourage it. I like to use objects as bags-of-methods, and avoid traditional OOP architectures.

That said, classes are useful and ES6 syntax sugar does make them much easier to use. The biggest improvement is to class inheritance.

Class inheritance in ES5 is particularly painful. Calling parent methods involves dynamic scoping shenanigans with .call, and you have to be careful when defining your constructor.

ES6 improves class inheritance with extends. And super lets you refer to the parent object at any time. Please don't get carried away with class hierarchies.

Getters/Setters

Getters and setters are one of those "Whoa that's cool ... what do I do with it?". They let you make methods pretend to be class properties. Or to wrap properties in functions, if you prefer.

There's nothing like it in ES5, but similar features do exist in other languages.

The concept goes like this: you define an object property that is actually a function, or a pair of functions. With getters, it's a neat way to manipulate data when it's accessed. With setters, it's a neat way to validate data when it's set.

You could just use functions directly, but this is a nice abstraction. MobX, for example, uses this to implement its state management machinery.

Modules

ES6 gave us a native way to organize our code into modules: import and export. It's a cleaner and more feature-full way of doing require(), which was never part of the official spec anyway.

What I like about modules, is that you can explicitly define what you're taking out of a file. Look at the top of the file and you know exactly what it uses. No more fishing around to find actual use cases.

Another nice feature is that you can give your own names to imported things. This is especially useful when semantic meaning of a function or class differs between modules. It happens more than you'd think.

These code samples don't work in CodePen, so you'll have to take my word for it.

Data Structures

JavaScript finally got real hashmaps and sets. No longer will we have to rely on pretending that objects are hashes, and, well, sets didn't even exist. So yeah ...

Both give you some nice ways to interact with your data, are generally more robust against weird things happening (I'm looking at you object values that become functions by accident), and are almost never used in real code. Not that I've seen at least.

Sets aren't that handy in most frontend web development, and the terser syntax of "objects as hashes" means most people keep using them. But these are good features that made many people excited so here we are. :)

Helpful array functions

ES2016 added the much needed includes function to arrays. That's not a big update, so I thought I'd show you some helpful Array functions you might not have known existed. Some have been there for a while. All have become more useful with the addition of arrow functions.

I often see people reach for Lodash or similar when they need these primitivies, but there's no need. Unless you have huge performance needs on the scale of millions of operations per second.

Promises

Promises are ES6's answer to callback hell. That's when you have too many nested anonymous callbacks and can't tell what's going on anymore. Promises fix that by giving your callbacks some structure.

You can think of them as monads, if you like. An object that holds the future, but doesn't have it yet. You can access that future by calling .then on a Promise. The important thing to keep in mind is that everything you return from a promises, is automatically wrapped in a promise.

Where Promises really shine is error handling and ensuring all promises in a set are resolved before doing stuff. For error handling you can use the combination of reject and .catch, for ensuring completion of all promises, you use .all.

Async/Await

Async/await is ES2017's answer to Promises hell. I think. Not sure anyone was complaining about Promises hell, but async/await is a nicer way to use Promises. The so called "synchronous way".

You can tag functions as async and use them as if they were normal functions. To access their future value, you use await. Similar concepts to Promises, but fewer parentheses. Once more you can think of async/await as monads, if that helps.

When it comes to error handling async/await puts us back in the era of throw/catch. Some people like that, some don't, but it's not much different than using .catch.

It doesn't add much to ensuring a set of async functions finishes. You still have to use Promise.all for that.

Swizec Teller

Thank you for reading my ES6, ES2016, and ES2017 cheatsheet. I hope it's helped you make sense of modern JavaScript syntax and behavior.