← Back to Blogs

Modern JavaScript (ES6+): Essential Features Every Developer Needs

Write Cleaner, More Expressive and Powerful JavaScript Code

Modern JavaScript (ES6 and beyond) introduced profound improvements to the language. Explore block scoping, arrow functions, destructuring, optional chaining, array manipulation, and modular architecture.


Part 1 - Block Scoping: let & const vs. var

Before ECMAScript 2015 (ES6), variables declared with var were function-scoped and hoisted to the top of their scope, causing infamous bugs with loops and closures.

Modern JavaScript introduces block scoping with let and const:

// 1. const: Block-scoped, cannot be reassigned (Default choice)
const API_URL = "https://api.example.com";
// API_URL = "new"; // TypeError: Assignment to constant variable

// Note: Objects declared with const CAN have their properties mutated!
const user = { name: "Somil" };
user.name = "Somil Sharma"; // Valid!

// 2. let: Block-scoped, reassignable (Use only when value changes)
let score = 0;
score += 10; // Valid!

// 3. Block Scope in Action
if (true) {
    let blockScoped = "Hidden from outside";
    var functionScoped = "Leaked outside!";
}
console.log(functionScoped); // "Leaked outside!"
// console.log(blockScoped); // ReferenceError!

Part 2 - Arrow Functions & Lexical 'this'

Arrow functions provide a concise syntax for writing function expressions and solve the classic this binding headaches in object methods and event handlers.

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

// Arrow Function (Implicit Return)
const addModern = (a, b) => a + b;

// Lexical 'this' binding
const timer = {
    seconds: 0,
    start() {
        // Arrow function inherits 'this' from enclosing start() method!
        setInterval(() => {
            this.seconds++;
            console.log(this.seconds);
        }, 1000);
    }
};

Part 3 - Destructuring & The Spread/Rest Operators

1. Object & Array Destructuring

const developer = {
    firstName: "Somil",
    role: "Frontend Engineer",
    country: "India",
    skills: ["HTML5", "CSS3", "JavaScript", "Canvas"]
};

// Unpacking properties with renaming and default values
const { firstName, role: jobTitle, theme = "dark" } = developer;
console.log(firstName, jobTitle, theme); // "Somil", "Frontend Engineer", "dark"

// Array destructuring
const [primarySkill, secondarySkill, ...remainingSkills] = developer.skills;
console.log(primarySkill);     // "HTML5"
console.log(remainingSkills);  // ["JavaScript", "Canvas"]

2. The Spread Operator (...)

// Combining arrays without mutating originals
const frontend = ['HTML', 'CSS'];
const fullstack = [...frontend, 'JavaScript', 'Node.js'];

// Shallow cloning and merging objects
const defaultSettings = { theme: 'dark', notifications: true };
const userSettings = { ...defaultSettings, notifications: false, sound: 'on' };

Part 4 - High-Order Array Methods (map, filter, reduce)

Modern functional JavaScript avoids manual for loops in favor of declarative, immutable array methods:

const products = [
    { id: 1, name: 'Laptop', price: 999, inStock: true },
    { id: 2, name: 'Mouse', price: 29, inStock: true },
    { id: 3, name: 'Keyboard', price: 79, inStock: false },
    { id: 4, name: 'Monitor', price: 299, inStock: true }
];

// 1. filter(): Keep items that match condition
const available = products.filter(p => p.inStock);

// 2. map(): Transform each element into something new
const productTitles = available.map(p => p.name.toUpperCase());

// 3. reduce(): Accumulate array into a single calculation
const totalCartValue = available.reduce((acc, item) => acc + item.price, 0);
console.log(`Cart Total: $${totalCartValue}`); // Cart Total: $1327

// 4. find() and some()
const mouse = products.find(p => p.name === 'Mouse');
const hasExpensiveItem = products.some(p => p.price > 500); // true

Part 5 - Optional Chaining (?.) & Nullish Coalescing (??)

Safely navigate nested data structures and assign bulletproof fallback defaults without repetitive conditional checks:

const userProfile = {
    account: {
        profile: {
            displayName: "Somil"
        }
    }
};

// Optional Chaining (?.)
// If any property in chain is null or undefined, stops immediately and returns undefined
const userAvatar = userProfile?.account?.profile?.avatar?.url;
console.log(userAvatar); // undefined (No crash!)

// Nullish Coalescing (??)
// Fallback applies ONLY when value is null or undefined (preserves 0, false, and '')
const customPort = 0;
const port = customPort ?? 3000; 
console.log(port); // 0 (Unlike || which would wrongly fallback to 3000!)

Part 6 - ES Modules (import / export)

Native modular JavaScript enables clean code organization and separation of concerns:

// utils.js - Named and default exports
export const add = (a, b) => a + b;
export const multiply = (a, b) => a * b;

export default class Logger {
    static log(msg) { console.log(`[LOG]: ${msg}`); }
}

// main.js - Importing dependencies
import Logger, { add, multiply } from './utils.js';

Logger.log(add(5, 10)); // [LOG]: 15