Published May 28, 2026 · 8 min read · 🏷️ JavaScript

JavaScript ES6+ Features Every Developer Should Know

ES6 (ECMAScript 2015) and later versions introduced features that transformed how we write JavaScript. This guide covers the ones you'll use daily.

Arrow Functions

Concise syntax and lexical this binding:

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

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

// With body (return needed)
const multiply = (a, b) => {
  return a * b;
};

// No arguments
const logSomething = () => console.log('Hello');

// Single argument (parentheses optional)
const double = x => x * 2;

Arrow functions don't have their own this — they inherit it from the enclosing scope.

Destructuring

Extract values from objects and arrays:

// Object destructuring
const user = { name: 'Alice', age: 25, city: 'NYC' };
const { name, age } = user;

// With renaming
const { name: userName, age: userAge } = user;

// Array destructuring
const colors = ['red', 'green', 'blue'];
const [first, second] = colors;

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

// Function parameters
function greet({ name, city }) {
  return `Hello ${name} from ${city}`;
}

Spread and Rest Operators

Same syntax, different contexts:

// Spread in arrays
const a = [1, 2, 3];
const b = [4, 5, 6];
const combined = [...a, ...b];  // [1, 2, 3, 4, 5, 6]

// Spread in objects
const obj1 = { x: 1, y: 2 };
const obj2 = { ...obj1, z: 3 };  // { x: 1, y: 2, z: 3 }

// Rest in function parameters
function sum(...numbers) {
  return numbers.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3, 4);  // 10

// Rest in destructuring
const [first, ...rest] = [1, 2, 3, 4];
// first = 1, rest = [2, 3, 4]

Template Literals

String interpolation and multi-line strings:

const name = 'Alice';
const age = 25;

// String interpolation
const greeting = `Hello, ${name}! You are ${age}.`;

// Multi-line strings
const html = `
  

${name}

Age: ${age}

`; // Expressions const price = 99.99; const message = `Total: $${(price * 1.1).toFixed(2)}`;

Default Parameters

function createUser(name, role = 'user', active = true) {
  return { name, role, active };
}
createUser('Bob');
// { name: 'Bob', role: 'user', active: true }

Object Shorthand

const name = 'Alice';
const age = 25;

// Old way
const user = { name: name, age: age };

// New way (when key and variable name match)
const user = { name, age };

// Method shorthand
const calculator = {
  value: 0,
  add(n) { this.value += n; },
  subtract(n) { this.value -= n; }
};

Modules (Import/Export)

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

// Named exports
import { PI, add } from './math.js';

// Default import
import Calculator from './math.js';

// All exports
import * as math from './math.js';
math.PI;

Async/Await

Cleaner syntax for Promises:

// With Promises
function fetchUser(id) {
  return fetch(`/api/users/${id}`)
    .then(res => res.json())
    .then(user => user.name);
}

// With async/await
async function fetchUser(id) {
  const res = await fetch(`/api/users/${id}`);
  const user = await res.json();
  return user.name;
}

// Error handling
async function fetchUser(id) {
  try {
    const res = await fetch(`/api/users/${id}`);
    if (!res.ok) throw new Error('User not found');
    return await res.json();
  } catch (error) {
    console.error('Failed:', error);
  }
}

Optional Chaining and Nullish Coalescing

// Optional chaining (ES2020)
const city = user?.address?.city ?? 'Unknown';

// Before optional chaining
const city = user && user.address && user.address.city;

// Nullish coalescing (ES2020)
const name = user.nickname ?? user.name ?? 'Anonymous';

// Before nullish coalescing
const name = user.nickname || user.name || 'Anonymous';
// Problem: 0 or '' would be treated as falsy

Array Methods: map, filter, reduce

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

// map - transform each element
const doubled = numbers.map(n => n * 2);

// filter - keep elements matching condition
const evens = numbers.filter(n => n % 2 === 0);

// reduce - accumulate to single value
const sum = numbers.reduce((acc, n) => acc + n, 0);

// Chaining
const result = numbers
  .filter(n => n > 2)
  .map(n => n * 10)
  .reduce((a, b) => a + b, 0);

Frequently Asked Questions

What's the difference between == and ===?

== coerces types (5 == '5' is true). === requires same type and value (5 === '5' is false). Always use === — type coercion has weird edge cases ([] == false is true, [0] == false is true, but [1,2] == false is false). The only common valid use of == is == null to check both null and undefined.

What are arrow functions good for and not good for?

Arrow functions are great for short callbacks and methods that don't need their own 'this' binding: [1,2,3].map(n => n*2). They're bad when you need 'this' (object methods, constructors, event handlers in classes) because arrows inherit 'this' lexically. They also can't be used as constructors — new (() => {}) throws.

When should I use async/await vs .then()?

async/await is more readable for sequential logic: const data = await fetch(url). Use .then() for fire-and-forget or when you explicitly want parallel execution: Promise.all(urls.map(u => fetch(u))). Mixing them is fine but be careful with error handling — async/await needs try/catch, .then() chains need .catch().

← Back to Blog
Copied!