# The ES6 Guide

> Discover the major features added in ES6 (ES2015): arrow functions, promises, classes, let and const, modules, destructuring, and the spread operator.

Author: [Flavio Copes](https://flaviocopes.com/about/) | Published: 2018-09-30 | Updated: 2026-07-18 | Topics: [JavaScript](https://flaviocopes.com/tags/js/) | Canonical: https://flaviocopes.com/es6/

<!-- TOC -->

- [Arrow Functions](#arrow-functions)
- [Lexical `this`](#lexical-this)
- [Promises](#promises)
- [Generators](#generators)
- [`let` and `const`](#let-and-const)
- [Classes](#classes)
  - [Constructor](#constructor)
  - [Super](#super)
  - [Getters and setters](#getters-and-setters)
- [Modules](#modules)
  - [Importing modules](#importing-modules)
  - [Exporting modules](#exporting-modules)
- [Template Literals](#template-literals)
- [Default parameters](#default-parameters)
- [The spread operator](#the-spread-operator)
- [Destructuring assignments](#destructuring-assignments)
- [Enhanced object literals](#enhanced-object-literals)
  - [Simpler syntax to include variables](#simpler-syntax-to-include-variables)
  - [Prototype](#prototype)
  - [super()](#super-1)
  - [Dynamic properties](#dynamic-properties)
- [`for...of` loop](#forof-loop)
- [Map and Set](#map-and-set)
- [New String methods](#new-string-methods)
- [New Object methods](#new-object-methods)

<!-- /TOC -->

ECMAScript 2015, also known as ES6, is a fundamental version of the ECMAScript standard.

It introduced arrow functions, classes, promises, modules, `let`, `const`, destructuring, and many other features that modern JavaScript uses every day.

ES6 and ES2015 refer to the same language edition. ES6 is the edition number, while ES2015 is the year-based name.

This article describes its most important additions. You can also read the [ECMAScript 2015 specification](https://262.ecma-international.org/6.0/).

## Arrow Functions

Arrow functions since their introduction changed how most JavaScript code looks (and works).

Visually, it's a simple and welcome change, from:

```js
const something = function something() {
  //...
}
```

to

```js
const something = () => {
  //...
}
```

And if the function body is a one-liner, just:

```js
const something = () => doSomething()
```

Also, if you have a single parameter, you could write:

```js
const something = param => doSomething(param)
```

This is not a breaking change, regular `function`s will continue to work just as before.

## Lexical `this`

Arrow functions don't create their own `this` value. They inherit `this` from the surrounding scope.

Regular functions get their `this` value from how they are called. This makes arrow functions useful for callbacks, but usually a poor choice for object methods.

See the [MDN arrow function reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) for the other differences.

## Promises

Promises (check the [full guide to promises](https://flaviocopes.com/javascript-promises/)) help us avoid deeply nested callbacks. ES2017 later added `async` and `await` as a cleaner syntax for consuming them.

Promises have been used by JavaScript developers well before ES2015, with many different libraries implementations (e.g. [jQuery](https://flaviocopes.com/jquery/), q, deferred.js, vow...), and the standard put a common ground across differences.

By using promises you can rewrite this code

```js
setTimeout(function() {
  console.log('I promised to run after 1s')
  setTimeout(function() {
    console.log('I promised to run after 2s')
  }, 1000)
}, 1000)
```

as

```js
const wait = () => new Promise(resolve => {
  setTimeout(resolve, 1000)
})

wait().then(() => {
  console.log('I promised to run after 1s')
  return wait()
})
.then(() => console.log('I promised to run after 2s'))
```

## Generators

Generators are a special kind of function with the ability to pause itself, and resume later, allowing other code to run in the meantime.

See the full [JavaScript Generators](https://flaviocopes.com/javascript-generators/) Guide for a detailed explanation of the topic.

## let and const

`var` is traditionally **function scoped**.

`let` is a new variable declaration which is **block scoped**.

This means a `let` variable declared in a loop, an `if`, or a plain block does not escape that block. A `var` declaration is available throughout its containing function.

`const` is block scoped like `let`, but its binding cannot be reassigned.

The value itself is not necessarily immutable. You can still change the properties of an object stored in a `const` variable. The [MDN `const` reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const) explains this distinction.

In JavaScript moving forward, you'll see little to no `var` declarations any more, just `let` and `const`.

My advice is to use `const` by default, and use `let` when you need to reassign the variable.

## Classes

JavaScript uses prototype-based inheritance. Developers coming from class-based languages can find this unfamiliar.

ES2015 introduced class syntax built on top of JavaScript's prototype system.

Now inheritance is very easy and resembles other object-oriented programming languages:

```js
class Person {
  constructor(name) {
    this.name = name
  }

  hello() {
    return 'Hello, I am ' + this.name + '.'
  }
}

class Actor extends Person {
  hello() {
    return super.hello() + ' I am an actor.'
  }
}

const tomCruise = new Actor('Tom Cruise')
tomCruise.hello()
```

(the above program prints "_Hello, I am Tom Cruise. I am an actor._")

ES2015 classes commonly initialize instance properties in the constructor. Public class fields were added to JavaScript later.

### Constructor

Classes have a special method called `constructor` which is called when a class is initialized via `new`.

### Super

Call `super()` in a derived constructor to run the parent constructor. Inside a method, use `super.methodName()` to call a parent method.

### Getters and setters

A getter for a property can be declared as

```js
class Person {
  get fullName() {
    return `${this.firstName} ${this.lastName}`
  }
}
```

Setters are written in the same way:

```js
class Person {
  set age(years) {
    this.theAge = years
  }
}
```

## Modules

Before ES2015, JavaScript used several competing module formats:

- AMD
- CommonJS

RequireJS is a loader that implements AMD, not a separate module format.

ES2015 added a standard module syntax. In browsers, a file must be loaded as a module, for example with `<script type="module">`.

### Importing modules

Importing is done via the `import ... from ...` construct:

```js
import myDefault from './my-module.js'
import * as myModule from './my-module.js'
import { value, doSomething } from './my-module.js'
import { doSomething as run } from './my-module.js'
```

See the [MDN `import` reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) for every supported form.

### Exporting modules

You can write modules and export anything to other modules using the `export` keyword:

```js
export const number = 2
export function doSomething() {
  //...
}
```

## Template Literals

Template literals are a new syntax to create strings:

```js
const aString = `A string`
```

They provide a way to embed expressions into strings, effectively inserting the values, by using the `${a_variable}` syntax:

```js
const joe = 'test'
const string = `something ${joe}` //something test
```

You can perform more complex expressions as well:

```js
const string = `something ${1 + 2 + 3}`
const string2 = `something ${doSomething() ? 'x' : 'y' }`
```

and strings can span over multiple lines:

```js
const string3 = `Hey
this

string
is awesome!`
```

Compare how we used to do multiline strings pre-ES2015:

```js
var str = 'One\n' +
'Two\n' +
'Three'
```

> [See this post for an in-depth guide on template literals](https://flaviocopes.com/javascript-template-literals/)

## Default parameters

Functions now support default parameters:

```js
const someFunction = function(index = 0, testing = true) { /* ... */ }
someFunction()
```

## The spread operator

You can expand an iterable, such as an array or string, using the spread operator `...`.

Let's start with an array example. Given

```js
const a = [1, 2, 3]
```

you can create a new array using

```js
const b = [...a, 4, 5, 6]
```

You can also create a copy of an array using

```js
const c = [...a]
```

Object spread looks similar, but it was added after ES2015. You can use it to make a shallow copy:

```js
const newObj = { ...oldObj }
```

Using strings, the spread operator creates an array with each char in the string:

```js
const hey = 'hey'
const arrayized = [...hey] // ['h', 'e', 'y']
```

This operator has some pretty useful applications. The most important one is the ability to use an array as function argument in a very simple way:

```js
const f = (arg1, arg2) => {}
const a = [1, 2]
f(...a)
```

(in the past you could do this using `f.apply(null, a)` but that's not as nice and readable)

## Destructuring assignments

Given an object, you can extract just some values and put them into named variables:

```js
const person = {
  firstName: 'Tom',
  lastName: 'Cruise',
  actor: true,
  age: 54, //made up
}

const {firstName: name, age} = person
```

`name` and `age` contain the desired values.

The syntax also works on arrays:

```js
const a = [1,2,3,4,5]
const [first, second] = a
```

This statement creates 3 new variables by getting the items with index 0, 1, 4 from the array `a`:

```js
const [first, second, , , fifth] = a
```

## Enhanced object literals

In ES2015 Object Literals gained superpowers.

### Simpler syntax to include variables

Instead of doing

```js
const something = 'y'
const x = {
  something: something
}
```

you can do

```js
const something = 'y'
const x = {
  something
}
```

### Prototype

A prototype can be specified with

```js
const anObject = { y: 'y' }
const x = {
  __proto__: anObject
}
```

### super()

```js
const anObject = { y: 'y', test: () => 'zoo' }
const x = {
  __proto__: anObject,
  test() {
    return super.test() + 'x'
  }
}
x.test() //zoox
```

### Dynamic properties

```js
const x = {
  ['a' + '_' + 'b']: 'z'
}
x.a_b //z
```

## `for...of` loop

ES5 back in 2009 introduced `forEach()` loops. While nice, they offered no way to break, like `for` loops always did.

ES2015 introduced the **`for-of` loop**, which combines the conciseness of `forEach` with the ability to break:

```js
//iterate over the value
for (const v of ['a', 'b', 'c']) {
  console.log(v)
}

//get the index as well, using `entries()`
for (const [i, v] of ['a', 'b', 'c'].entries()) {
  console.log(i, v)
}
```

## Map and Set

[**Map**](https://flaviocopes.com/javascript-data-structures-map/) and [**Set**](https://flaviocopes.com/javascript-data-structures-set/) (and their respective garbage collected **WeakMap** and **WeakSet**) are the official implementations of two very popular data structures.

## New String methods

Any string value got some new instance methods:

- `repeat()` repeats the string a specified number of times: `'Ho'.repeat(3) //HoHoHo`
- `codePointAt()` handles retrieving the Unicode code of characters that cannot be represented by a single 16-bit UTF-16 unit, but need 2 instead

## New Object methods

ES6 introduced several static methods under the Object namespace:

- [`Object.is()`](https://flaviocopes.com/javascript-object-is/) determines if two values are the same value
- [`Object.assign()`](https://flaviocopes.com/javascript-object-assign/) used to shallow copy an object
- `Object.setPrototypeOf()` sets an object's prototype
