JavaScript ES6+ Modern Features
JavaScript has evolved significantly with ES6 (ECMAScript 2015) and later versions. These modern features make JavaScript more powerful and easier to work with.
What is ES6+?
ES6 (ECMAScript 2015) was a major update to JavaScript that introduced many new features. Since then, JavaScript gets annual updates (ES2016, ES2017, ES2018, etc.) with even more capabilities.
Why Modern JavaScript Matters
- Better Code: Write cleaner, more readable code
- Industry Standard: Most companies expect modern JS knowledge
- Framework Requirements: React, Vue, Angular require modern JS
- Future-Proof: Stay current with web development
Variable Declarations
let and const
Replace var with better alternatives:
// Old way (avoid)
var name = "John";
name = "Jane"; // Can be reassigned unexpectedly
var hobbies = ["coding", "reading"];
// Modern way (preferred)
let name = "John"; // Can be reassigned
const birthYear = 1990; // Cannot be reassigned
const hobbies = ["coding", "reading"]; // Array itself is const
// Block scope demonstration
if (true) {
let blockVar = "I exist only here";
const blockConst = "Me too";
}
console.log(blockVar); // ReferenceError: blockVar is not defined
Destructuring
Extract values from arrays and objects easily:
// Array destructuring
const colors = ["red", "green", "blue"];
const [first, second, third] = colors;
console.log(first); // red
console.log(third); // blue
// Skip values
const [primary, , secondary] = colors;
console.log(secondary); // green
// Object destructuring
const person = {
name: "Alice",
age: 30,
city: "New York"
};
const { name, age, city } = person;
console.log(name, age, city); // Alice 30 New York
// Default values and renaming
const { name: userName = "Guest", country = "USA" } = person;
console.log(userName, country); // Alice USA
Spread Operator
Copy and combine arrays/objects easily:
// Array spreading
const fruits = ["apple", "banana"];
const vegetables = ["carrot", "broccoli"];
const allFood = [...fruits, ...vegetables];
console.log(allFood); // ["apple", "banana", "carrot", "broccoli"]
// Copy array with new item
const newFruits = [...fruits, "orange"];
console.log(newFruits); // ["apple", "banana", "orange"]
// Object spreading
const basicUser = { name: "Bob", age: 25 };
const userDetails = { email: "bob@example.com", city: "Boston" };
const fullUser = { ...basicUser, ...userDetails };
console.log(fullUser);
// { name: "Bob", age: 25, email: "bob@example.com", city: "Boston" }
// Override properties
const updatedUser = { ...fullUser, age: 26 };
console.log(updatedUser);
// { name: "Bob", age: 26, email: "bob@example.com", city: "Boston" }
Arrow Functions
Basic Arrow Functions
Concise syntax for function expressions:
// Traditional function
function add(a, b) {
return a + b;
}
// Arrow function
const add = (a, b) => {
return a + b;
};
// Concise arrow function (single expression)
const add = (a, b) => a + b;
// Single parameter (no parentheses needed)
const square = x => x * x;
// No parameters
const greet = () => "Hello, World!";
console.log(add(5, 3)); // 8
console.log(square(4)); // 16
console.log(greet()); // Hello, World!
Arrow Functions and this
Arrow functions don’t have their own this binding:
// Traditional function
const person = {
name: "Alice",
hobbies: ["coding", "reading"],
greet: function() {
console.log(`Hello, I'm ${this.name}`);
this.hobbies.forEach(function(hobby) {
console.log(`${this.name} likes ${hobby}`); // this is undefined
});
}
};
// Arrow function solution
person.greet = function() {
console.log(`Hello, I'm ${this.name}`);
this.hobbies.forEach(hobby => {
console.log(`${this.name} likes ${hobby}`); // this refers to person
});
};
person.greet();Template Literals
String Interpolation
Create strings with variables and expressions:
// Old way (concatenation)
const name = "John";
const age = 25;
const message = "Hello, my name is " + name + " and I'm " + age + " years old.";
// Modern way (template literals)
const message = `Hello, my name is ${name} and I'm ${age} years old.`;
console.log(message);
// Expressions in template literals
const price = 19.99;
const quantity = 3;
const total = `Total: $${(price * quantity).toFixed(2)}`;
// Multi-line strings
const html = `
<div>
<h1>Welcome</h1>
<p>This is much cleaner than concatenation</p>
</div>
`;Tagged Template Literals
Custom processing of template literals:
function highlight(strings, ...values) {
return strings.reduce((result, string, i) => {
const value = values[i] ? `<mark>${values[i]}</mark>` : '';
return result + string + value;
}, '');
}
const name = "Alice";
const age = 30;
const highlighted = highlight`My name is ${name} and I'm ${age} years old.`;
console.log(highlighted);
// My name is <mark>Alice</mark> and I'm <mark>30</mark> years old.
Enhanced Object Methods
Property Shorthand
const name = "John";
const age = 30;
// Old way
const person = {
name: name,
age: age,
greet: function() {
return `Hello, I'm ${this.name}`;
}
};
// Modern shorthand
const person = {
name, // Same as name: name
age, // Same as age: age
greet() { // Same as greet: function() {
return `Hello, I'm ${this.name}`;
}
};
console.log(person.greet()); // Hello, I'm John
Computed Property Names
const property = "dynamic";
const value = "I'm dynamic";
const obj = {
[property]: value, // Computed property name
[`prefix_${property}`]: "Another value",
[Date.now()]: "timestamp key"
};
console.log(obj.dynamic); // I'm dynamic
Object Methods
const person = {
name: "Alice",
age: 30
};
// Get keys
const keys = Object.keys(person);
console.log(keys); // ["name", "age"]
// Get values
const values = Object.values(person);
console.log(values); // ["Alice", 30]
// Get entries
const entries = Object.entries(person);
console.log(entries); // [["name", "Alice"], ["age", 30]]
// Object.assign (merging)
const details = { email: "alice@example.com", city: "New York" };
const merged = Object.assign({}, person, details);
console.log(merged);
// { name: "Alice", age: 30, email: "alice@example.com", city: "New York" }
// Spread operator (preferred)
const mergedModern = { ...person, ...details };
console.log(mergedModern);Array Methods
Array.from()
Create arrays from array-like objects:
// From string
const name = "JavaScript";
const letters = Array.from(name);
console.log(letters); // ["J", "a", "v", "a", "S", "c", "r", "i", "p", "t"]
// From arguments
function sum() {
return Array.from(arguments).reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4)); // 10
// With mapping function
const numbers = [1, 2, 3];
const doubled = Array.from(numbers, x => x * 2);
console.log(doubled); // [2, 4, 6]
Array.find() and Array.findIndex()
const users = [
{ id: 1, name: "Alice", age: 30 },
{ id: 2, name: "Bob", age: 25 },
{ id: 3, name: "Charlie", age: 35 }
];
// Find user by ID
const user = users.find(u => u.id === 2);
console.log(user); // { id: 2, name: "Bob", age: 25 }
// Find index
const userIndex = users.findIndex(u => u.age > 30);
console.log(userIndex); // 2
Array.includes()
const fruits = ["apple", "banana", "orange"];
const hasApple = fruits.includes("apple");
const hasGrape = fruits.includes("grape");
console.log(hasApple); // true
console.log(hasGrape); // false
// With fromIndex
const hasOrange = fruits.includes("orange", 1); // Search from index 1
console.log(hasOrange); // true
Enhanced Function Parameters
Default Parameters
// Old way
function greet(name) {
name = name || "Guest";
return `Hello, ${name}!`;
}
// Modern way
function greet(name = "Guest") {
return `Hello, ${name}!`;
}
console.log(greet()); // Hello, Guest!
console.log(greet("Alice")); // Hello, Alice!
Rest Parameters
Handle unlimited arguments:
// Old way (arguments object)
function sum() {
const args = Array.prototype.slice.call(arguments);
return args.reduce((total, num) => total + num, 0);
}
// Modern way (rest parameters)
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // 15
// Mixed with regular parameters
function createUser(name, ...hobbies) {
return {
name,
hobbies,
hobbyCount: hobbies.length
};
}
const user = createUser("Alice", "coding", "reading", "gaming");
console.log(user);
// { name: "Alice", hobbies: ["coding", "reading", "gaming"], hobbyCount: 3 }
Async/Await
Basic Async/Await
Modern way to handle asynchronous operations:
// Old way with promises
function fetchData() {
return fetch('/api/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
}
// Modern way with async/await
async function fetchData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}Multiple Async Operations
// Fetch data sequentially
async function fetchUserData() {
const userResponse = await fetch('/api/user/1');
const user = await userResponse.json();
const postsResponse = await fetch(`/api/users/${user.id}/posts`);
const posts = await postsResponse.json();
return { user, posts };
}
// Fetch data in parallel
async function fetchParallel() {
const [userResponse, postsResponse] = await Promise.all([
fetch('/api/user/1'),
fetch('/api/posts')
]);
const user = await userResponse.json();
const posts = await postsResponse.json();
return { user, posts };
}Async with Loops
const urls = [
'/api/posts/1',
'/api/posts/2',
'/api/posts/3'
];
// Sequential processing
async function processSequentially() {
for (const url of urls) {
const response = await fetch(url);
const data = await response.json();
console.log('Processed:', data);
}
}
// Parallel processing
async function processInParallel() {
const promises = urls.map(url => fetch(url).then(res => res.json()));
const results = await Promise.all(promises);
results.forEach(data => console.log('Processed:', data));
}Modules
Import/Export
Organize code into reusable modules:
// math.js - Export module
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 calculator(operation, a, b) {
switch (operation) {
case 'add': return add(a, b);
case 'multiply': return multiply(a, b);
default: return null;
}
}
// main.js - Import module
import calculator, { add, multiply, PI } from './math.js';
console.log(add(5, 3)); // 8
console.log(multiply(4, 6)); // 24
console.log(PI); // 3.14159
const result = calculator('add', 10, 20);
console.log(result); // 30
Dynamic Imports
Load modules on demand:
// Load module only when needed
async function loadMath() {
if (needMath) {
const math = await import('./math.js');
return math.add(5, 3);
}
return null;
}
// Default export with dynamic import
async function loadDefault() {
const calculator = await import('./math.js');
return calculator.default('multiply', 4, 5);
}Classes
Class Syntax
class Person {
// Constructor
constructor(name, age) {
this.name = name;
this.age = age;
}
// Method
greet() {
return `Hello, I'm ${this.name} and I'm ${this.age} years old.`;
}
// Getter
get birthYear() {
const currentYear = new Date().getFullYear();
return currentYear - this.age;
}
// Setter
set age(newAge) {
if (newAge > 0 && newAge < 120) {
this._age = newAge;
}
}
// Static method
static fromObject(obj) {
return new Person(obj.name, obj.age);
}
}
// Using the class
const person = new Person("Alice", 30);
console.log(person.greet()); // Hello, I'm Alice and I'm 30 years old.
console.log(person.birthYear); // 1994
const personFromObj = Person.fromObject({ name: "Bob", age: 25 });
console.log(personFromObj.greet()); // Hello, I'm Bob and I'm 25 years old.
Inheritance
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return `${this.name} makes a sound`;
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // Call parent constructor
this.breed = breed;
}
// Override parent method
speak() {
return `${this.name} barks`;
}
// New method
fetch() {
return `${this.name} fetches the ball`;
}
}
const dog = new Dog("Buddy", "Golden Retriever");
console.log(dog.speak()); // Buddy barks
console.log(dog.fetch()); // Buddy fetches the ball
Destructuring Advanced
Function Return Destructuring
function getUser() {
return {
name: "Alice",
age: 30,
city: "New York",
hobbies: ["coding", "reading"]
};
}
// Destructure function return
const { name, age, city } = getUser();
console.log(`${name} is ${age} years old and lives in ${city}`);
// Destructure with rename
const { name: userName, city: location } = getUser();
console.log(`${userName} lives in ${location}`);
// Destructure nested objects
const { name, hobbies: [firstHobby] } = getUser();
console.log(`${name}'s first hobby is ${firstHobby}`);Array Destructuring Advanced
const users = [
{ id: 1, name: "Alice", active: true },
{ id: 2, name: "Bob", active: false },
{ id: 3, name: "Charlie", active: true }
];
// Find and destructure in one step
const activeUser = users.find(({ active }) => active);
const { name: activeUserName } = activeUser || {};
console.log(activeUserName); // Alice
// Skip elements with rest
const [first, , third, ...rest] = [1, 2, 3, 4, 5];
console.log(first, third); // 1 3
console.log(rest); // [4, 5]
Practical Examples
Modern Shopping Cart
class ShoppingCart {
constructor() {
this.items = [];
this.total = 0;
}
addItem(product, quantity = 1) {
const existingItem = this.items.find(item => item.id === product.id);
if (existingItem) {
existingItem.quantity += quantity;
} else {
this.items.push({ ...product, quantity });
}
this.calculateTotal();
}
removeItem(productId) {
this.items = this.items.filter(item => item.id !== productId);
this.calculateTotal();
}
calculateTotal() {
this.total = this.items.reduce((sum, item) => {
return sum + (item.price * item.quantity);
}, 0);
}
checkout() {
return {
items: [...this.items],
total: this.total
};
}
}
// Usage
const cart = new ShoppingCart();
cart.addItem({ id: 1, name: "Laptop", price: 999 });
cart.addItem({ id: 2, name: "Mouse", price: 29 }, 2);
cart.removeItem(1);
const order = cart.checkout();
console.log(order);Modern API Client
class ApiClient {
constructor(baseURL) {
this.baseURL = baseURL;
this.cache = new Map();
}
async request(endpoint, options = {}) {
const url = `${this.baseURL}${endpoint}`;
const cacheKey = `${endpoint}${JSON.stringify(options)}`;
// Check cache first
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey);
}
try {
const response = await fetch(url, {
headers: {
'Content-Type': 'application/json',
...options.headers
},
...options
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
// Cache the result
this.cache.set(cacheKey, data);
return data;
} catch (error) {
console.error('API request failed:', error);
throw error;
}
}
// Convenient methods
async get(endpoint) {
return this.request(endpoint);
}
async post(endpoint, data) {
return this.request(endpoint, {
method: 'POST',
body: JSON.stringify(data)
});
}
}
// Usage
const api = new ApiClient('https://api.example.com');
const user = await api.get('/users/1');
const newUser = await api.post('/users', {
name: 'John',
email: 'john@example.com'
});Browser Compatibility
Checking Support
// Feature detection
const supportsArrowFunctions = (() => {}).toString().includes('=>');
const supportsAsyncAwait = async () => {}.constructor.name === 'AsyncFunction';
const supportsClasses = class {}.constructor === 'function';
console.log('Arrow functions:', supportsArrowFunctions);
console.log('Async/await:', supportsAsyncAwait);
console.log('Classes:', supportsClasses);Polyfill Example
// Array.includes polyfill for older browsers
if (!Array.prototype.includes) {
Array.prototype.includes = function(searchElement, fromIndex) {
const array = Object(this);
const len = array.length >>> 0;
const from = fromIndex | 0;
for (let i = from; i < len; i++) {
if (array[i] === searchElement) {
return true;
}
}
return false;
};
}Best Practices
Use Modern Features Judiciously
// Use const by default
const API_KEY = 'your-api-key';
const MAX_ATTEMPTS = 3;
// Use let only when you need to reassign
let counter = 0;
counter++;
// Use default parameters
function createUser({ name = 'Guest', role = 'user' } = {}) {
return { name, role };
}
// Use destructuring for cleaner code
function processUser({ name, age, email }) {
console.log(name, age, email);
}
// Use async/await instead of promise chains
async function fetchUserData() {
try {
const user = await fetchUser();
const posts = await fetchUserPosts(user.id);
return { user, posts };
} catch (error) {
console.error('Failed to fetch user data:', error);
}
}
// Use template literals for string building
const message = `Hello ${name}, you have ${unreadCount} new messages.`;External Resources:
Related Tutorials: