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