Modern JavaScript: Essential ES6+ Features
JavaScript has evolved significantly since ES6 (ES2015). If you learned JavaScript years ago or are still writing ES5-style code, these modern features will transform how you write JavaScript. Let’s explore the most important ones.
let and const
Say goodbye to var. Use let for variables that change and const for constants.
// Old way
var name = "Alice";
var age = 30;
// Modern way
let name = "Alice"; // Can be reassigned
const age = 30; // Cannot be reassigned
Why it matters:
constprevents accidental reassignment- Block scoping prevents bugs
- Makes code intent clearer
Arrow Functions
A concise syntax for writing functions:
// Old way
function add(a, b) {
return a + b;
}
// Arrow function
const add = (a, b) => a + b;
// With multiple lines
const greet = (name) => {
const message = `Hello, ${name}!`;
return message;
};
// Single parameter (parentheses optional)
const double = x => x * 2;
Bonus: Arrow functions don’t create their own this context:
class Timer {
constructor() {
this.seconds = 0;
setInterval(() => {
this.seconds++; // 'this' refers to Timer instance
}, 1000);
}
}
Template Literals
String interpolation and multiline strings:
// Old way
const name = "Alice";
const greeting = "Hello, " + name + "!";
// Template literals
const greeting = `Hello, ${name}!`;
// Multiline
const html = `
<div>
<h1>Welcome, ${name}</h1>
<p>You have ${messages.length} new messages</p>
</div>
`;
// Expressions
const price = `Total: $${(quantity * price).toFixed(2)}`;
Destructuring
Extract values from objects and arrays:
// Object destructuring
const user = { name: "Alice", age: 30, city: "NYC" };
const { name, age } = user;
// With renaming
const { name: userName, age: userAge } = user;
// With defaults
const { country = "USA" } = user;
// Array destructuring
const colors = ["red", "green", "blue"];
const [first, second] = colors; // "red", "green"
// Skip elements
const [, , third] = colors; // "blue"
// Rest operator
const [primary, ...others] = colors; // "red", ["green", "blue"]
// Function parameters
function greet({ name, age }) {
return `${name} is ${age} years old`;
}
greet(user);
Spread Operator
Expand arrays and objects:
// Array spreading
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]
// Copy array
const copy = [...arr1];
// Object spreading
const defaults = { theme: "dark", lang: "en" };
const userPrefs = { lang: "fr" };
const settings = { ...defaults, ...userPrefs };
// { theme: "dark", lang: "fr" }
// Function arguments
const numbers = [1, 2, 3];
Math.max(...numbers); // 3
Rest Parameters
Collect multiple arguments into an array:
function sum(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
}
sum(1, 2, 3, 4); // 10
// With other parameters
function multiply(multiplier, ...numbers) {
return numbers.map(n => n * multiplier);
}
multiply(2, 1, 2, 3); // [2, 4, 6]
Default Parameters
Provide default values for function parameters:
// Old way
function greet(name) {
name = name || "Guest";
return "Hello, " + name;
}
// Modern way
function greet(name = "Guest") {
return `Hello, ${name}`;
}
// With expressions
function createUser(name, role = "user", id = generateId()) {
return { name, role, id };
}
Object Shorthand
Concise object creation:
const name = "Alice";
const age = 30;
// Old way
const user = {
name: name,
age: age,
greet: function() {
return "Hello";
}
};
// Shorthand
const user = {
name,
age,
greet() {
return "Hello";
}
};
Promises
Handle async operations:
// Creating a promise
function fetchUser(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id > 0) {
resolve({ id, name: "Alice" });
} else {
reject("Invalid ID");
}
}, 1000);
});
}
// Using promises
fetchUser(1)
.then(user => console.log(user))
.catch(error => console.error(error));
// Chaining
fetchUser(1)
.then(user => fetchPosts(user.id))
.then(posts => console.log(posts))
.catch(error => console.error(error));
Async/Await
Cleaner async code:
// With promises (callback hell)
fetchUser(1)
.then(user => {
return fetchPosts(user.id);
})
.then(posts => {
return fetchComments(posts[0].id);
})
.then(comments => {
console.log(comments);
});
// With async/await
async function loadUserData() {
try {
const user = await fetchUser(1);
const posts = await fetchPosts(user.id);
const comments = await fetchComments(posts[0].id);
console.log(comments);
} catch (error) {
console.error(error);
}
}
// Parallel requests
async function loadData() {
const [users, posts] = await Promise.all([
fetchUsers(),
fetchPosts()
]);
}
Classes
Object-oriented programming in JavaScript:
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
greet() {
return `Hello, I'm ${this.name}`;
}
// Getter
get displayName() {
return this.name.toUpperCase();
}
// Static method
static create(data) {
return new User(data.name, data.email);
}
}
// Inheritance
class Admin extends User {
constructor(name, email, permissions) {
super(name, email);
this.permissions = permissions;
}
greet() {
return `${super.greet()} - I'm an admin`;
}
}
Modules
Organize code into reusable modules:
// math.js
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export default function multiply(a, b) {
return a * b;
}
// main.js
import multiply, { add, PI } from './math.js';
// Or import everything
import * as Math from './math.js';
Array Methods
Powerful array manipulation:
const numbers = [1, 2, 3, 4, 5];
// map: Transform each element
const doubled = numbers.map(n => n * 2);
// filter: Keep elements that pass test
const evens = numbers.filter(n => n % 2 === 0);
// reduce: Combine into single value
const sum = numbers.reduce((total, n) => total + n, 0);
// find: Get first matching element
const first = numbers.find(n => n > 3); // 4
// some: Check if any pass test
const hasEven = numbers.some(n => n % 2 === 0); // true
// every: Check if all pass test
const allPositive = numbers.every(n => n > 0); // true
// includes: Check if value exists
const hasThree = numbers.includes(3); // true
// Chaining
const result = numbers
.filter(n => n % 2 === 0)
.map(n => n * 2)
.reduce((sum, n) => sum + n, 0);
Optional Chaining
Safely access nested properties:
const user = {
name: "Alice",
address: {
city: "NYC"
}
};
// Old way
const zip = user && user.address && user.address.zip;
// Optional chaining
const zip = user?.address?.zip; // undefined (no error)
// With arrays
const firstPost = user?.posts?.[0];
// With functions
const result = obj.method?.(); // Only call if method exists
Nullish Coalescing
Default values that only trigger on null/undefined:
// Old way (problematic with falsy values)
const count = userInput || 10; // 10 if userInput is 0, "", false
// Nullish coalescing (only null/undefined)
const count = userInput ?? 10; // 0 if userInput is 0
Object Methods
// Object.entries()
const user = { name: "Alice", age: 30 };
Object.entries(user); // [["name", "Alice"], ["age", 30]]
// Object.keys()
Object.keys(user); // ["name", "age"]
// Object.values()
Object.values(user); // ["Alice", 30]
// Object.fromEntries()
const entries = [["name", "Bob"], ["age", 25]];
Object.fromEntries(entries); // { name: "Bob", age: 25 }
Start Using These Today
You don’t need to learn everything at once. Start with:
- let/const - Use instead of var
- Arrow functions - For cleaner callbacks
- Template literals - For strings
- Destructuring - Extract values easily
- async/await - For async operations
These features are supported in all modern browsers and Node.js. For older browsers, use Babel to transpile your code.
Resources
- MDN JavaScript Guide
- javascript.info - Comprehensive tutorial
- ES6 Features - Side-by-side comparisons
- Practice on CodeWars or LeetCode
Modern JavaScript makes code more readable, maintainable, and powerful. Start incorporating these features into your projects today!