JavaScript
Intermediate

JavaScript ES6+ Essential Features

25 min read

Master modern JavaScript features including arrow functions, destructuring, template literals, async/await, and more. Essential knowledge for every JavaScript developer.

What You'll Learn

Arrow functions and lexical this
Template literals and string interpolation
Destructuring arrays and objects
Spread and rest operators
Promises and async/await
Modules and imports/exports

Introduction to ES6+

ES6 (ECMAScript 2015) introduced many powerful features that revolutionized JavaScript development. These features make code more readable, concise, and maintainable. Let's explore the most important ones.

Arrow Functions

Arrow functions provide a more concise way to write functions and have different behavior with the this keyword.

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

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

// Multiple lines
const multiply = (a, b) => {
  const result = a * b;
  return result;
};

// Single parameter (parentheses optional)
const square = x => x * x;

// No parameters
const greet = () => "Hello, World!";

// Array methods with arrow functions
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2);
const evens = numbers.filter(n => n % 2 === 0);
const sum = numbers.reduce((acc, n) => acc + n, 0);

console.log(doubled); // [2, 4, 6, 8, 10]
console.log(evens);   // [2, 4]
console.log(sum);     // 15

Lexical This

// Traditional function - 'this' changes context
const obj = {
  name: 'Alice',
  hobbies: ['reading', 'coding'],
  
  // This won't work as expected
  showHobbies: function() {
    this.hobbies.forEach(function(hobby) {
      console.log(this.name + ' likes ' + hobby); // 'this' is undefined
    });
  },
  
  // Arrow function preserves 'this'
  showHobbiesArrow: function() {
    this.hobbies.forEach(hobby => {
      console.log(this.name + ' likes ' + hobby); // Works correctly
    });
  }
};

Template Literals

Template literals make string interpolation and multi-line strings much easier to work with.

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

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

// Multi-line strings
const html = `
  <div class="card">
    <h2>${name}</h2>
    <p>Age: ${age}</p>
  </div>
`;

// Expressions in template literals
const price = 19.99;
const tax = 0.08;
const total = `Total: $${(price * (1 + tax)).toFixed(2)}`;

Destructuring

Destructuring allows you to extract values from arrays and objects into distinct variables.

Array Destructuring

// Basic array destructuring
const colors = ['red', 'green', 'blue'];
const [first, second, third] = colors;

console.log(first);  // 'red'
console.log(second); // 'green'
console.log(third);  // 'blue'

// Skip elements
const [primary, , tertiary] = colors;
console.log(primary);  // 'red'
console.log(tertiary); // 'blue'

// Default values
const [a, b, c, d = 'yellow'] = colors;
console.log(d); // 'yellow'

// Rest operator
const numbers = [1, 2, 3, 4, 5];
const [head, ...tail] = numbers;
console.log(head); // 1
console.log(tail); // [2, 3, 4, 5]

// Swapping variables
let x = 1, y = 2;
[x, y] = [y, x];
console.log(x, y); // 2, 1

Object Destructuring

// Basic object destructuring
const person = {
  name: 'Alice',
  age: 30,
  city: 'New York',
  country: 'USA'
};

const { name, age } = person;
console.log(name); // 'Alice'
console.log(age);  // 30

// Renaming variables
const { name: fullName, age: years } = person;
console.log(fullName); // 'Alice'
console.log(years);    // 30

// Default values
const { name, age, profession = 'Developer' } = person;
console.log(profession); // 'Developer'

// Function parameters
function greetUser({ name, age = 25 }) {
  console.log(`Hello ${name}, you are ${age} years old`);
}

greetUser({ name: 'Charlie', age: 35 });

Spread and Rest Operators

The spread (...) operator allows you to expand arrays and objects, while the rest operator collects multiple elements.

// Spread with arrays
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];
console.log(combined); // [1, 2, 3, 4, 5, 6]

// Copy array
const original = [1, 2, 3];
const copy = [...original];

// Spread with objects
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const merged = { ...obj1, ...obj2 };
console.log(merged); // { a: 1, b: 2, c: 3, d: 4 }

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

console.log(sum(1, 2, 3, 4)); // 10

Promises and Async/Await

Modern JavaScript provides elegant ways to handle asynchronous operations.

// Creating a promise
const fetchData = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const success = Math.random() > 0.5;
      if (success) {
        resolve({ data: 'Hello, World!' });
      } else {
        reject(new Error('Failed to fetch data'));
      }
    }, 1000);
  });
};

// Using async/await
async function getData() {
  try {
    const result = await fetchData();
    console.log('Data received:', result.data);
    return result;
  } catch (error) {
    console.error('Error:', error.message);
    throw error;
  }
}

// Multiple async operations
async function fetchMultipleData() {
  try {
    // Parallel execution
    const [result1, result2] = await Promise.all([
      fetchData(),
      fetchData()
    ]);
    
    return { result1, result2 };
  } catch (error) {
    console.error('Error in fetchMultipleData:', error);
  }
}

Modules (Import/Export)

ES6 modules allow you to organize your code into separate files and import/export functionality.

// math.js - Named exports
export const PI = 3.14159;

export function add(a, b) {
  return a + b;
}

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

// Default export
export default function subtract(a, b) {
  return a - b;
}

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

console.log(add(5, 3));      // 8
console.log(subtract(10, 4)); // 6
console.log(PI);             // 3.14159

// Import all
import * as MathUtils from './math.js';
console.log(MathUtils.PI);   // 3.14159

Enhanced Object Literals

const name = 'Alice';
const age = 30;

// Shorthand property names
const person = { name, age };
// Same as: { name: name, age: age }

// Method definitions
const calculator = {
  // Old way
  add: function(a, b) {
    return a + b;
  },
  
  // New way
  subtract(a, b) {
    return a - b;
  }
};

// Default function parameters
function greet(name = 'World', greeting = 'Hello') {
  return `${greeting}, ${name}!`;
}

console.log(greet());              // "Hello, World!"
console.log(greet('Alice'));       // "Hello, Alice!"
console.log(greet('Bob', 'Hi'));   // "Hi, Bob!"

Practical Example: Task Manager

Let's combine these features in a practical example - a simple task manager using modern JavaScript.

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

  // Add task with default priority
  addTask(title, priority = 'medium') {
    const task = {
      id: Date.now(),
      title,
      priority,
      completed: false,
      createdAt: new Date()
    };
    
    this.tasks = [...this.tasks, task];
    return task;
  }

  // Get tasks by priority using destructuring
  getTasksByPriority(priority) {
    return this.tasks.filter(({ priority: taskPriority }) => 
      taskPriority === priority
    );
  }

  // Update task using spread operator
  updateTask(id, updates) {
    this.tasks = this.tasks.map(task =>
      task.id === id ? { ...task, ...updates } : task
    );
  }

  // Async method to save tasks
  async saveTasks() {
    try {
      const response = await fetch('/api/tasks', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(this.tasks)
      });
      
      if (!response.ok) {
        throw new Error('Failed to save tasks');
      }
      
      return await response.json();
    } catch (error) {
      console.error('Save failed:', error);
      throw error;
    }
  }

  // Get task summary using template literals
  getSummary() {
    const total = this.tasks.length;
    const completed = this.tasks.filter(({ completed }) => completed).length;
    const pending = total - completed;
    
    return `Tasks: ${total} total, ${completed} completed, ${pending} pending`;
  }
}

// Usage example
const taskManager = new TaskManager();

// Add tasks
taskManager.addTask('Learn ES6', 'high');
taskManager.addTask('Build project', 'medium');
taskManager.addTask('Write tests');

// Get high priority tasks
const highPriorityTasks = taskManager.getTasksByPriority('high');
console.log(highPriorityTasks);

// Update task
taskManager.updateTask(highPriorityTasks[0].id, { completed: true });

// Get summary
console.log(taskManager.getSummary());

// Save tasks (async)
taskManager.saveTasks()
  .then(() => console.log('Tasks saved successfully'))
  .catch(error => console.error('Failed to save:', error));

Next Steps

You've learned the essential ES6+ features that every modern JavaScript developer should know. Here are some areas to explore next:

  • Advanced async patterns (generators, async iterators)
  • Proxy and Reflect APIs
  • WeakMap and WeakSet
  • Symbol primitive type
  • Optional chaining and nullish coalescing
  • Dynamic imports and code splitting

💡 Practice Tip

Start using these features in your daily coding. Try refactoring old JavaScript code to use modern ES6+ syntax. The more you practice, the more natural these patterns will become.

Built with v0