Skip to content
Home
JavaScript ES6+ Features Every Developer Should Know

JavaScript ES6+ Features Every Developer Should Know

JavaScript JavaScript 8 min read 1505 words Beginner ExcellentWiki Editorial Team

ECMAScript 2015 (ES6) was the largest update to JavaScript in the language’s history. It introduced features that fundamentally changed how JavaScript is written — replacing verbose patterns with concise, expressive syntax. Subsequent yearly releases (ES2016 through ES2024) have continued to refine and expand the language.

This guide covers the most impactful ES6+ features that every JavaScript developer should know and use daily.

let and const

Before ES6, var was the only way to declare variables. ES6 introduced let and const with block scoping, eliminating many of var’s pitfalls:

// Block scoping
{
  let x = 1;
  const y = 2;
  var z = 3;
---
console.log(z); // 3
console.log(x); // ReferenceError
console.log(y); // ReferenceError

// const prevents reassignment (but not mutation)
const API_URL = "https://api.example.com";
const user = { name: "Alice" };
user.name = "Bob";  // allowed
user = {};          // TypeError

Use const by default, let when you need reassignment, and never var in new code.

Arrow Functions

Arrow functions provide a concise syntax and lexically bind this:

// Concise syntax
const add = (a, b) => a + b;
const square = x => x * x;
const greet = () => "Hello!";

// Block body for multi-line
const sum = (a, b) => {
  const result = a + b;
  return result;
---;

The key difference from regular functions is how they handle this:

const timer = {
  delay: 1000,
  start: function() {
    // Arrow function inherits this from start()
    setTimeout(() => {
      console.log(this.delay); // 1000
    }, this.delay);

    // Regular function has its own this
    setTimeout(function() {
      console.log(this.delay); // undefined
    }, this.delay);
  }
---;

Arrow functions cannot be used as constructors and lack their own arguments object. Use regular functions for object methods that need dynamic this binding.

Template Literals

Template literals use backticks and support interpolation and multiline strings:

const name = "Alice";
const age = 30;

// Interpolation
const message = `Hello, ${name}! You are ${age} years old.`;

// Expressions
const result = `The total is ${items.reduce((a, b) => a + b, 0)}`;

// Multiline without \n
const html = `
  <div>
    <h1>${title}</h1>
  </div>
`;

// Tagged templates
function highlight(strings, ...values) {
  return strings.reduce((acc, str, i) =>
    `${acc}${str}<strong>${values[i] || ""}</strong>`, "");
---

const name = "Alice";
highlight`Hello, ${name}!`; // "Hello, <strong>Alice</strong>!"

Destructuring

Destructuring extracts values from arrays and objects into distinct variables:

// Object destructuring
const user = { id: 1, name: "Alice", email: "alice@example.com" };
const { name, email } = user;
console.log(name);  // "Alice"

// Renaming
const { name: userName, email: userEmail } = user;

// Default values
const { role = "user" } = user;

// Nested destructuring
const response = { data: { id: 1, attributes: { title: "Post" } } };
const { data: { attributes: { title } } } = response;

// Array destructuring
const colors = ["red", "green", "blue"];
const [first, second] = colors;
console.log(first);  // "red"

// Skipping elements
const [, , third] = colors;

// Rest in destructuring
const [head, ...tail] = [1, 2, 3, 4];
console.log(tail); // [2, 3, 4]

Destructuring is especially powerful in function parameters:

function renderUser({ name, email, role = "user" }) {
  console.log(`${name} (${email}) — ${role}`);
---

renderUser({ name: "Alice", email: "alice@example.com" });

Spread and Rest Operators

The ... syntax serves two purposes depending on context.

Spread — Expands an iterable

// Arrays
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]
const copy = [...arr1];

// Objects (ES2018)
const defaults = { theme: "light", lang: "en" };
const userPrefs = { theme: "dark" };
const config = { ...defaults, ...userPrefs }; // { theme: "dark", lang: "en" }

// Function calls
const numbers = [4, 8, 15];
console.log(Math.max(...numbers)); // 15

Rest — Collects remaining values

// Function parameters
function sum(prefix, ...numbers) {
  return `${prefix}: ${numbers.reduce((a, b) => a + b, 0)}`;
---
console.log(sum("Total", 1, 2, 3, 4)); // "Total: 10"

// Destructuring
const { a, b, ...rest } = { a: 1, b: 2, c: 3, d: 4 };
console.log(rest); // { c: 3, d: 4 }

Modules

ES6 modules provide a standardized way to organize and share code:

// 📁 math.js
export const PI = 3.14159;
export function add(a, b) { return a + b; }
export default class Calculator { /* ... */ }

// 📁 app.js
import Calculator, { PI, add } from "./math.js";
import * as math from "./math.js";

console.log(add(1, 2));      // 3
console.log(math.PI);        // 3.14159

Modules are always in strict mode, are deferred by default, and support static analysis for tree shaking. Use named exports for utilities and default exports for the primary value of a module.

Classes

ES6 classes are syntactic sugar over JavaScript’s prototypal inheritance:

class Animal {
  constructor(name) {
    this.name = name;
  }

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

  static create(name) {
    return new Animal(name);
  }
---

class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }

  speak() {
    console.log(`${this.name} barks!`);
  }

  get description() {
    return `${this.name} is a ${this.breed}`;
  }

  set breed(value) {
    if (!value) throw new Error("Breed required");
    this._breed = value;
  }
---

const dog = new Dog("Rex", "German Shepherd");
dog.speak(); // "Rex barks!"
console.log(dog.description); // "Rex is a German Shepherd"

Private fields (ES2022) use the # prefix:

class BankAccount {
  #balance = 0;

  deposit(amount) {
    this.#balance += amount;
  }

  getBalance() {
    return this.#balance;
  }
---

Promises

ES6 introduced native promises for asynchronous programming:

function fetchUser(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const user = { id, name: "Alice" };
      resolve(user);
      // or reject(new Error("Not found"));
    }, 1000);
  });
---

fetchUser(1)
  .then(user => console.log(user))
  .catch(err => console.error(err))
  .finally(() => console.log("Done"));

Promise combinators added in later versions:

  • Promise.all — resolves when all resolve, rejects on any rejection
  • Promise.allSettled — resolves when all settle (resolve or reject)
  • Promise.race — settles with the first settled promise
  • Promise.any — resolves with the first fulfilled promise (ES2021)

Additional ES6+ Features

Default Parameters

function greet(name = "Guest", greeting = "Hello") {
  return `${greeting}, ${name}!`;
---

Enhanced Object Literals

const name = "Alice";
const user = {
  name,                         // shorthand property
  greet() {                     // shorthand method
    console.log(`Hi, I'm ${this.name}`);
  },
  ["dynamic_" + Date.now()]: "value" // computed property key
---;

Map and Set

const map = new Map();
map.set("key", "value");
map.get("key"); // "value"

const set = new Set([1, 2, 2, 3]);
console.log([...set]); // [1, 2, 3]

Symbols

const unique = Symbol("id");
const obj = { [unique]: "secret" };

Optional Chaining (ES2020)

const city = user?.address?.city ?? "Unknown";

Nullish Coalescing (ES2020)

const count = input ?? 0;  // only replaces null/undefined, not other falsy values
const old = input || 0;    // replaces any falsy value (empty string, 0, false)

Logical Assignment (ES2021)

x ||= y;  // x || (x = y)
x &&= y;  // x && (x = y)
x ??= y;  // x ?? (x = y)

Array Methods

// find and findIndex
const found = items.find(item => item.id === 3);
const index = items.findIndex(item => item.id === 3);

// flat and flatMap
const nested = [[1, 2], [3, 4]];
console.log(nested.flat());       // [1, 2, 3, 4]
console.log(nested.flatMap(x => x.map(n => n * 2))); // [2, 4, 6, 8]

// includes
console.log([1, 2, 3].includes(2)); // true

Summary

ES6+ transformed JavaScript from a language that required boilerplate into one that is concise, expressive, and pleasant to write. let and const provide sane scoping, arrow functions simplify callbacks, destructuring reduces repetitive access patterns, modules organize code cleanly, and classes offer a familiar OOP syntax. Each successive ECMAScript release has added thoughtful refinements — optional chaining, nullish coalescing, private fields, and array methods — that make everyday JavaScript development more productive. Mastering these features is the key to writing modern, idiomatic JavaScript.

FAQ

What is the difference between var, let, and const?

var is function-scoped and hoisted. let and const are block-scoped. const cannot be reassigned (but objects can mutate). Prefer const by default, use let when reassignment is needed, and avoid var in modern code.

How does the JavaScript event loop work?

The event loop continuously checks the call stack and task queues. Macrotasks (setTimeout, I/O) are processed one per loop iteration. All microtasks (Promise callbacks, queueMicrotask) are processed between macrotasks. UI rendering occurs after microtask processing.

What is the difference between == and ===?

== performs type coercion before comparison (1 == ’1’ is true). === requires both value and type to be identical (1 === ’1’ is false). Always use === unless you explicitly need type coercion.

When should I use promises vs async/await?

Async/await is syntactic sugar over promises, making asynchronous code read synchronously. Prefer async/await for readability. Use promise combinators (Promise.all, Promise.race) when managing multiple concurrent operations.

What are the most common JavaScript memory leaks?

Global variables, forgotten timers/intervals, detached DOM nodes, closures retaining large objects, and event listeners not removed. Use Chrome DevTools Memory panel to identify leaks. WeakMap/WeakSet help with cache-related leaks.

For a comprehensive overview, read our article on Javascript Arrays Guide.

For a comprehensive overview, read our article on Javascript Async Await.

Section: JavaScript 1505 words 8 min read Beginner 756 articles in section Report inaccuracy Back to top