Navigate back to the homepage
📚 Books

The best ES6+ features

Mark Pollmann
October 12th, 2017 · 2 min read


JavaScript has changed a lot in the last couple years with the biggest update coming in 2015 called EcmaScript Version 6. From then on new features were released on a yearly basis. In my mind these additions change the language from a toy language to something I love to code in everyday. Here I will present my favorite features of modern JavaScript, for a full list you can check out the language specification here.

let and const

Let’s start with a quick one. The var keyword in JavaScript has a couple gotchas because of it being function- and not block-scoped. One quick example:

1for (var i = 0; i < 5; i++) {
2 // your code
4console.log(i); // 5

We arguably want i to be confined to the for-expression. let does it correctly:

1for (var i = 0; i < 5; i++) {
2 // your code
4console.log(i); // ReferenceError


  • are block- not function-scoped.
  • are not hoisted like vars.
    This means if you call them in a block before the declaration you’ll get a reference error:
1console.log(foo); // ReferenceError
2let foo = 5;

while the same is ok with a var declaration:

1console.log(foo); // undefined (no error, though)
2var foo = 5;
  • Makes shadowing inside functions possible.
  • It’s not possible to re-declare a let in same block in strict mode while var allows it (which is probably not what you want):
1var foo = 1;
2var foo = 1; // OK
4let bar = 1;
5let bar = 1; // SyntaxError


const works like let only that the variable binding is fixed, so a redeclaration is not possible. Note that const objects are still mutable, just not the binding. If you need immutable objects you can take a look at immutable.js.

1const foo = 1;
2foo = 5; // TypeError
3foo++; // TypeError
5const bar = { a: 5 };
6bar = { b: 6 }; // TypeError
7bar.c = 10; // OK. { a:5, c:10 }

Try to use const whenever you can. When somebody reads your code and sees a const they can be sure this variable binding will not change which means less mental overhead for them.

There are more subtle changes with the introduction of let and const which I might talk about in a future blog post. As a rule, though, always use let and const instead of var and prefer const to let.

Default function arguments

This is such a convenient functionality known from other languages like Python. In ES6 it looks like this (featuring template strings):

1function greet(name = "Mr X") {
2 console.log(`Hello ${name}`);
4greet("Hans"); // Hello Hans
5greet(); // Hello Mr X

Before ES6 providing your own default arguments was clumsy and error-prone (remember that checking for a value with !foo could lead to errors because of falsy values:

1function greet(name) {
2 if (name === undefined) {
3 var name = "Mr X";
4 }
5 console.log("Hello" + name);

Arrow functions

Fat arrow functions reduce the boilerplate of anonymous functions:

1const add = (a, b) => a + b;
2const inc = a => a + 1; // dropping the parens is valid if there's only one argument

No function keyword needed. Also no return needed if it’s just a one-liner. If you need more space just surround the body in parenthesis and work with a return keyword:

1const foo = (a, b) => {
2 // do your thing
3 return a + b;

Additionally they don’t bind this which removes the need of the often-seen var self = this construct in nested functions.

Template literals

These are used to

  • Build dynamic strings containing variables (or any expression really..)
  • Multi-line strings without having to end the line with a backslash
1const name = "Fritz";
2console.log(`Hello ${name}`); // 'Hello Fritz'
4console.log(`First line
5Second line
6Third line
7`); // returns the same three lines

There’s also an advanced usage of this feature: Tagged template literals where you process the string with a function beforehand. Styled Components uses this feature extensively, for example.


Use this feature when you quickly want to pull out values from an array or object:

1// Array destructuring:
2const arr = [21, 42, 57];
3// get them all:
4const [foo, bar, baz] = arr;
5// or just the second one:
6const [, second];
7console.log(bar) // 42
8console.log(second) // 42
10// Object destructuring:
11const obj = { a: 21, b: 42, c: 57 };
12const { a, b, c } = obj;
13console.log(c) // 57;

This is just the tip of the iceberg for destructuring. You can extract deeply nested values, rename them and lots more. I will write more about this in the next part of the series.

Browser support

You can check the compatibility here or here. Basically all the features are supported in modern browsers with the still used IE11 lagging behind. It’s still recommended to use a transpiler like babeljs. It has a new feature called babel-preset-env in which you can define which browsers you want to support and it figures out itself if the features have to be transpiled or used natively.

With babeljs there’s no reason to wait to use these awesome features so go and incorporate them in your workflow. If you want some practice take a look at ES6 Katas.

In the next post we will take a look at advanced destructuring, tagged template literals, the Object[key] setting syntax, new array functions and some interesting feature proposals.

More articles from Mark Pollmann

Using NodeJS with TypeScript in 2020

Note: This article is aimed at Node developers looking into TypeScript and is planned as a living document. When I find something noteworthy…

May 15th, 2020 · 4 min read

Migrating a React Codebase to TypeScript

Switching to TypeScript with create-react-app.

May 5th, 2020 · 3 min read
© 2017–2020 Mark Pollmann
Link to $ to $ to $