JavaScript ES6 Features
A practical guide to JavaScript ES6 (ECMAScript 2015): let and const, arrow functions, template literals, destructuring, classes, modules, promises, and more.
ECMAScript 6 (ES6) is a significant update to the JavaScript language that introduced many new features, making JavaScript more powerful and efficient. This guide provides a detailed exploration of ES6, offering comprehensive explanations and multiple code examples to help you master these new capabilities.
Introduction to ES6
ES6, also known as ECMAScript 2015, brought many improvements and new features to JavaScript. ECMAScript is the official standard that JavaScript implements, and ES6 was its largest revision since 1999. These changes made the language more modern and easier to work with, enhancing both readability and maintainability.
This guide walks through the features you will use most often:
- Variable declarations —
letandconst - Functions — arrow functions and default parameters
- Strings — template literals
- Working with data — destructuring, enhanced object literals, rest/spread
- Asynchronous code — promises
- Object-oriented code — classes and modules
- New collections and primitives —
Map,Set,WeakMap,WeakSet,Symbol, iterators, generators, and proxies
Today every modern browser and Node.js supports ES6 natively, so you can use these features without a build step. Each example below is runnable—edit it and press run to experiment.
Let and Const
One of the most notable changes in ES6 is the introduction of let and const for variable declaration. Before ES6, the only option was var, which is function-scoped and is hoisted in a way that often produces surprising bugs. let and const are block-scoped and behave more predictably, which is why modern code rarely uses var at all.
Let
The let keyword allows you to declare variables that are limited in scope to the block, statement, or expression in which they are used.
In the example above, x declared inside the if block is a separate variable from x declared outside. This demonstrates block-level scoping provided by let. With var, both assignments would refer to the same variable, and the outer console.log would print 20.
let and const are also subject to the temporal dead zone (TDZ): the variable exists from the start of the block but cannot be read or assigned until its declaration runs. Referencing it earlier throws a ReferenceError, which catches typos that var would silently turn into undefined.
Const
The const keyword is used to declare variables whose values are never intended to change. It is block-scoped like let.
In this example, attempting to reassign a value to y results in an error because const declares a constant whose value cannot be changed.
A common point of confusion: const prevents reassignment of the binding, not mutation of the value. An object or array declared with const can still have its contents changed—you just cannot point the variable at a different object.
Reach for const by default and only switch to let when you genuinely need to reassign. This prevents accidental changes and signals intent to anyone reading the code.
Arrow Functions
Arrow functions provide a concise syntax for writing function expressions. They also lexically bind the this value, making them particularly useful for writing short functions.
Here, the arrow function add takes two parameters and returns their sum. When the body is a single expression, you can drop the braces and the return keyword—the expression's value is returned automatically.
The bigger benefit is that arrow functions do not have their own this; they inherit it from the surrounding scope. Before arrow functions, callbacks frequently lost the expected this and developers worked around it with const self = this or .bind(this). Arrow functions make that boilerplate unnecessary:
Because they have no own this, avoid arrow functions for object methods that rely on this, and for event handlers where you expect this to be the element. Use a regular function in those cases.
Template Literals
Template literals make it easier to create strings, especially when embedding variables or expressions. They use backticks (```) instead of quotes.
This example shows how template literals allow you to embed the variable name directly into the string, improving readability and convenience. The ${ ... } placeholder can hold any expression, not just a variable—for example `Total: ${price * qty}`. Template literals also span multiple lines without \n, so you can write formatted output or small HTML snippets inline.
Default Parameters
Default parameters allow you to initialize function parameters with default values if no values are provided.
In this example, the multiply function assigns a default value of 1 to b if no second argument is provided, ensuring the function always returns a valid result.
Destructuring Assignment
Destructuring assignment is a convenient way of extracting values from arrays or objects into distinct variables.
Array Destructuring
This example shows how array destructuring allows you to assign the values of the numbers array to individual variables a, b, and c.
Object Destructuring
In this example, object destructuring extracts the name and age properties from the person object into separate variables.
Enhanced Object Literals
ES6 enhances object literals, allowing for a more concise and readable syntax.
Here, the object obj uses shorthand property names and method definitions, making the object literal more concise.
Promises
Promises provide a way to handle asynchronous operations more efficiently, avoiding callback hell and making the code more readable.
In this example, a promise is created that resolves after 1 second with the message 'Success!'. The then method is used to handle the resolved value.
Use promises to handle asynchronous operations cleanly and avoid callback hell.
Classes
ES6 introduced classes, providing a clearer and more concise syntax for creating objects and dealing with inheritance.
This example demonstrates how classes in ES6 offer a clean and intuitive syntax for defining objects and their behavior.
Modules
Modules allow you to break up your code into separate files and import/export functions, objects, or primitives between files.
Exporting
// math.js
export const add = (a, b) => a + b;Importing
// main.js
import { add } from './math.js';
console.log(add(2, 3)); // 5In these examples, the add function is exported from math.js and imported into main.js, demonstrating how modules facilitate code organization and reuse.
Rest and Spread Operators
Rest Operator
The rest operator (...) allows you to represent an indefinite number of arguments as an array.
In this example, the sum function uses the rest operator to gather all arguments into an array, making it easy to perform operations on them.
Spread Operator
The spread operator (...) allows an iterable (like an array) to be expanded in places where zero or more arguments or elements are expected.
This example shows how the spread operator can be used to combine arrays into a single array, simplifying the code.
Iterators and Generators
Iterators
Iterators are objects that allow you to traverse through a collection, such as an array or string.
In this example, the iterator provides a way to access each element in the array sequentially.
Generators
Generators are functions that can be paused and resumed, providing a powerful way to handle iterables.
This example shows how a generator function can yield values one at a time, pausing execution between each yield.
Symbols
Symbols are a new primitive data type introduced in ES6. They are unique and immutable, often used to create unique object keys.
In this example, symbol1 and symbol2 are unique, even though they have the same description.
Maps and Sets
Maps
Maps are collections of key-value pairs where the keys can be of any type.
This example demonstrates how to create a map, set key-value pairs, and retrieve values.
Sets
Sets are collections of unique values.
In this example, the set only contains unique values, even though 3 was added twice.
Proxies
Proxies allow you to create a proxy for another object, enabling you to intercept and redefine fundamental operations.
This example shows how a proxy can intercept property access, providing a custom response when the property is not found.
WeakMap and WeakSet
WeakMap
WeakMap is similar to Map but only allows objects as keys and does not prevent garbage collection if there are no other references to the key object.
WeakSet
WeakSet is similar to Set but only allows objects as values and does not prevent garbage collection if there are no other references to the object.
Conclusion
ES6 brings a wealth of features that make JavaScript a more robust and efficient language. By understanding and utilizing these features, developers can write cleaner, more maintainable code. This guide has provided an in-depth look at the most important aspects of ES6, complete with examples to help you get started.
To go deeper on the individual topics, continue with arrow functions, destructuring assignment, rest parameters and spread syntax, classes, modules, and promises.