Skip to main content

JavaScript ES6 Overview

Introduction

ECMAScript 6 (ES6), also known as ECMAScript 2015, was a significant update to JavaScript that introduced many new features and syntax improvements. Released in 2015, ES6 brought JavaScript more in line with other modern programming languages and addressed many long-standing issues. This update represented the biggest change to the language since its creation.

In this guide, we'll explore the most important ES6 features that revolutionized JavaScript development and continue to be fundamental to modern JavaScript programming.

Key ES6 Features

1. let and const Declarations

Before ES6, variables were declared using var, which had some confusing scoping rules. ES6 introduced two new ways to declare variables:

  • let: For variables that need to be reassigned
  • const: For constants that should not be reassigned
javascript
// Old way with var
var name = "JavaScript";
var version = 5;
version = 6; // Allowed

// New way with let and const
let language = "JavaScript";
language = "JS"; // Allowed - can be reassigned

const RELEASE_YEAR = 2015;
// RELEASE_YEAR = 2016; // Error! Cannot reassign a constant

The key advantage is that both let and const are block-scoped, unlike var which is function-scoped:

javascript
if (true) {
var x = 10; // Accessible outside this block
let y = 20; // Only accessible within this block
const z = 30; // Only accessible within this block
}

console.log(x); // 10
// console.log(y); // ReferenceError: y is not defined
// console.log(z); // ReferenceError: z is not defined

2. Arrow Functions

Arrow functions provide a shorter syntax for writing function expressions and don't have their own this context.

javascript
// Traditional function
function add(a, b) {
return a + b;
}

// Arrow function
const addArrow = (a, b) => a + b;

console.log(add(5, 3)); // Output: 8
console.log(addArrow(5, 3)); // Output: 8

With single parameters, you can even omit the parentheses:

javascript
// Traditional function
function square(x) {
return x * x;
}

// Arrow function
const squareArrow = x => x * x;

console.log(square(4)); // Output: 16
console.log(squareArrow(4)); // Output: 16

Arrow functions shine when used with array methods:

javascript
const numbers = [1, 2, 3, 4, 5];

// Traditional way
const doubled = numbers.map(function(num) {
return num * 2;
});

// With arrow function
const doubledArrow = numbers.map(num => num * 2);

console.log(doubledArrow); // Output: [2, 4, 6, 8, 10]

3. Template Literals

Template literals allow for easier string interpolation and multiline strings using backticks (`).

javascript
const name = 'Alice';
const age = 28;

// Old way
const greeting = 'Hello, my name is ' + name + ' and I am ' + age + ' years old.';

// With template literals
const greetingNew = `Hello, my name is ${name} and I am ${age} years old.`;

console.log(greetingNew); // Output: Hello, my name is Alice and I am 28 years old.

// Multiline strings
const multiline = `This is a string
that spans across
multiple lines.`;

console.log(multiline);
// Output:
// This is a string
// that spans across
// multiple lines.

4. Default Parameters

ES6 allows function parameters to have default values.

javascript
// Old way
function greet(name) {
name = name || 'Guest';
return `Hello, ${name}!`;
}

// With default parameters
function greetNew(name = 'Guest') {
return `Hello, ${name}!`;
}

console.log(greetNew()); // Output: Hello, Guest!
console.log(greetNew('Alice')); // Output: Hello, Alice!

5. Destructuring Assignment

Destructuring allows unpacking values from arrays or properties from objects into distinct variables.

Array Destructuring

javascript
// Old way
const colors = ['red', 'green', 'blue'];
const red = colors[0];
const green = colors[1];
const blue = colors[2];

// With destructuring
const [r, g, b] = ['red', 'green', 'blue'];

console.log(r); // Output: red
console.log(g); // Output: green
console.log(b); // Output: blue

Object Destructuring

javascript
// Old way
const person = { name: 'John', age: 30, city: 'New York' };
const personName = person.name;
const personAge = person.age;
const personCity = person.city;

// With destructuring
const { name, age, city } = { name: 'John', age: 30, city: 'New York' };

console.log(name); // Output: John
console.log(age); // Output: 30
console.log(city); // Output: New York

You can also rename properties:

javascript
const { name: fullName, age: years } = person;
console.log(fullName); // Output: John
console.log(years); // Output: 30

6. Spread and Rest Operators (...)

The ... syntax can be used as either spread or rest operator, depending on the context.

Spread Operator

Expands an iterable (like an array) into individual elements:

javascript
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5];

console.log(arr2); // Output: [1, 2, 3, 4, 5]

// Works with objects too
const obj1 = { x: 1, y: 2 };
const obj2 = { ...obj1, z: 3 };

console.log(obj2); // Output: { x: 1, y: 2, z: 3 }

Rest Operator

Collects multiple elements into an array:

javascript
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}

console.log(sum(1, 2, 3, 4, 5)); // Output: 15

7. Classes

ES6 introduced a new class syntax, making JavaScript's prototype-based inheritance easier to work with:

javascript
// Old prototype-based way
function AnimalOld(name) {
this.name = name;
}

AnimalOld.prototype.speak = function() {
return `${this.name} makes a sound.`;
};

// ES6 Class syntax
class Animal {
constructor(name) {
this.name = name;
}

speak() {
return `${this.name} makes a sound.`;
}
}

const dog = new Animal('Rex');
console.log(dog.speak()); // Output: Rex makes a sound.

// Inheritance is cleaner too
class Dog extends Animal {
constructor(name, breed) {
super(name); // Call parent constructor
this.breed = breed;
}

speak() {
return `${this.name} barks! It's a ${this.breed}.`;
}
}

const myDog = new Dog('Max', 'Labrador');
console.log(myDog.speak()); // Output: Max barks! It's a Labrador.

8. Promises

Promises provide a cleaner way to handle asynchronous operations, avoiding "callback hell":

javascript
// Old callback approach
function getDataOld(callback) {
setTimeout(() => {
callback('Data received');
}, 1000);
}

getDataOld(data => {
console.log(data);
});

// Using Promises
function getData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Data received');
// For errors: reject(new Error('Failed to get data'));
}, 1000);
});
}

getData()
.then(data => console.log(data))
.catch(error => console.error(error));

9. Modules

ES6 introduced a standardized module system for JavaScript:

javascript
// math.js
export function add(a, b) {
return a + b;
}

export function multiply(a, b) {
return a * b;
}

export const PI = 3.14159;

// main.js
import { add, multiply, PI } from './math.js';

console.log(add(2, 3)); // Output: 5
console.log(multiply(2, 3)); // Output: 6
console.log(PI); // Output: 3.14159

// You can also import everything
import * as math from './math.js';

console.log(math.add(2, 3)); // Output: 5
console.log(math.multiply(2, 3)); // Output: 6

Real-World Example: Building a Task Manager

Let's see how ES6 features can be combined to create a simple task manager:

javascript
class TaskManager {
constructor() {
this.tasks = [];
}

addTask(title, priority = 'normal') {
const newTask = {
id: Date.now(),
title,
priority,
completed: false,
createdAt: new Date()
};

this.tasks = [...this.tasks, newTask];
return newTask;
}

completeTask(taskId) {
this.tasks = this.tasks.map(task =>
task.id === taskId ? { ...task, completed: true } : task
);
}

deleteTask(taskId) {
this.tasks = this.tasks.filter(task => task.id !== taskId);
}

getSummary() {
const { completed, active } = this.tasks.reduce((summary, task) => {
if (task.completed) {
summary.completed++;
} else {
summary.active++;
}
return summary;
}, { completed: 0, active: 0 });

return `${completed} task(s) completed, ${active} task(s) still active.`;
}
}

// Usage
const manager = new TaskManager();

manager.addTask('Learn ES6', 'high');
manager.addTask('Build a project');
manager.addTask('Take a break', 'low');

console.log(manager.tasks);
// Output: Array of three task objects

manager.completeTask(manager.tasks[0].id);
console.log(manager.getSummary());
// Output: 1 task(s) completed, 2 task(s) still active.

manager.deleteTask(manager.tasks[2].id);
console.log(manager.tasks.length);
// Output: 2

Summary

ES6/ECMAScript 2015 brought significant improvements to JavaScript that made the language more powerful and developer-friendly:

  • let and const provide better variable scoping
  • Arrow functions offer concise syntax and lexical this
  • Template literals make string manipulation easier
  • Default parameters simplify function definitions
  • Destructuring provides elegant extraction of values
  • Spread/Rest operators enhance array and object operations
  • Classes enable clearer object-oriented programming
  • Promises improve asynchronous code readability
  • Modules create better code organization

These features form the foundation of modern JavaScript development and are widely used in frameworks like React, Vue, and Angular.

Additional Resources

  1. MDN Web Docs: ES6 Features
  2. ES6 Features Repository
  3. Babel REPL - Test ES6 code and see its ES5 equivalent

Exercises

  1. Convert a traditional function that calculates the area of a circle to an arrow function with a default radius of 1.
  2. Create a Person class with a constructor and a greet method, then create a Student class that extends Person.
  3. Write a function that uses destructuring to swap the values of two variables.
  4. Use template literals to create a multiline string that includes variables for a user's profile.
  5. Create a promise-based function that simulates fetching user data after a delay.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)