ES6+ Features
Modern JavaScript features that make code more concise, readable, and powerful.
Arrow Functions
javascript// Traditional function function add(a, b) { return a + b; } // Arrow function const add = (a, b) => a + b; // Single parameter (parentheses optional) const square = x => x * x; // No parameters const greet = () => 'Hello World!'; // Multiple statements const processUser = (user) => { const processed = user.name.toUpperCase(); return `Welcome, ${processed}!`; }; // Arrow functions in array methods const numbers = [1, 2, 3, 4, 5]; const doubled = numbers.map(n => n * 2); const evens = numbers.filter(n => n % 2 === 0); const sum = numbers.reduce((acc, n) => acc + n, 0); // 'this' binding difference class Timer { constructor() { this.seconds = 0; } start() { // Arrow function preserves 'this' setInterval(() => { this.seconds++; console.log(this.seconds); }, 1000); } }
Template Literals
javascriptconst name = 'Alice'; const age = 30; // Basic interpolation const greeting = `Hello, my name is ${name} and I'm ${age} years old.`; // Multi-line strings const html = ` <div class="user-card"> <h2>${name}</h2> <p>Age: ${age}</p> <p>Status: ${age >= 18 ? 'Adult' : 'Minor'}</p> </div> `; // Expression evaluation const price = 19.99; const tax = 0.08; const total = `Total: $${(price * (1 + tax)).toFixed(2)}`; // Tagged 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 searchTerm = 'JavaScript'; const text = highlight`Learn ${searchTerm} programming with ${name}`; // Result: "Learn <mark>JavaScript</mark> programming with <mark>Alice</mark>"
Enhanced Object Literals
javascriptconst name = 'John'; const age = 25; // Shorthand property names const user = { name, age }; // Same as { name: name, age: age } // Computed property names const propName = 'dynamicProp'; const obj = { [propName]: 'dynamic value', [`prefix_${name}`]: 'computed key' }; // Method shorthand const calculator = { // Old way: add: function(a, b) { return a + b; } add(a, b) { return a + b; }, subtract(a, b) { return a - b; }, // Getter and setter get result() { return this._result || 0; }, set result(value) { this._result = value; } }; // Dynamic method names const methodName = 'multiply'; const mathUtils = { [methodName](a, b) { return a * b; } };
Default Parameters
javascript// Basic default parameters function greet(name = 'Guest', greeting = 'Hello') { return `${greeting}, ${name}!`; } console.log(greet()); // Hello, Guest! console.log(greet('Alice')); // Hello, Alice! console.log(greet('Bob', 'Hi')); // Hi, Bob! // Default parameters with expressions function createUser(name, role = 'user', id = Date.now()) { return { name, role, id }; } // Default parameters can reference other parameters function buildUrl(protocol = 'https', domain, path = '/') { return `${protocol}://${domain}${path}`; } // Default parameters with destructuring function processOptions({ timeout = 5000, retries = 3, debug = false } = {}) { return { timeout, retries, debug }; } console.log(processOptions()); // { timeout: 5000, retries: 3, debug: false } console.log(processOptions({ timeout: 10000 })); // { timeout: 10000, retries: 3, debug: false }
Let and Const
javascript// Block scoping with let and const function demonstrateScoping() { var varVariable = 'var'; let letVariable = 'let'; const constVariable = 'const'; if (true) { var varInBlock = 'var in block'; let letInBlock = 'let in block'; const constInBlock = 'const in block'; console.log(varInBlock); // Accessible console.log(letInBlock); // Accessible console.log(constInBlock); // Accessible } console.log(varInBlock); // Accessible (hoisted) // console.log(letInBlock); // ReferenceError // console.log(constInBlock); // ReferenceError } // Temporal dead zone function temporalDeadZone() { // console.log(x); // ReferenceError: Cannot access 'x' before initialization let x = 5; console.log(x); // 5 } // const with objects and arrays const user = { name: 'Alice', age: 30 }; user.age = 31; // OK - modifying property user.city = 'New York'; // OK - adding property // user = {}; // TypeError - reassigning const const numbers = [1, 2, 3]; numbers.push(4); // OK - modifying array // numbers = []; // TypeError - reassigning const
Symbols
javascript// Creating symbols const sym1 = Symbol(); const sym2 = Symbol('description'); const sym3 = Symbol('description'); console.log(sym2 === sym3); // false - each symbol is unique // Symbol as object property const id = Symbol('id'); const user = { name: 'Alice', [id]: 12345 }; console.log(user[id]); // 12345 console.log(Object.keys(user)); // ['name'] - symbol properties are not enumerable // Well-known symbols const obj = { [Symbol.iterator]: function* () { yield 1; yield 2; yield 3; } }; for (const value of obj) { console.log(value); // 1, 2, 3 } // Global symbol registry const globalSym1 = Symbol.for('app.id'); const globalSym2 = Symbol.for('app.id'); console.log(globalSym1 === globalSym2); // true console.log(Symbol.keyFor(globalSym1)); // 'app.id'
Iterators and Generators
javascript// Generator function function* numberGenerator() { yield 1; yield 2; yield 3; return 'done'; } const gen = numberGenerator(); console.log(gen.next()); // { value: 1, done: false } console.log(gen.next()); // { value: 2, done: false } console.log(gen.next()); // { value: 3, done: false } console.log(gen.next()); // { value: 'done', done: true } // Infinite generator function* fibonacci() { let a = 0, b = 1; while (true) { yield a; [a, b] = [b, a + b]; } } const fib = fibonacci(); console.log(fib.next().value); // 0 console.log(fib.next().value); // 1 console.log(fib.next().value); // 1 console.log(fib.next().value); // 2 // Generator with parameters function* parameterizedGenerator() { const x = yield 'First yield'; const y = yield `Received: ${x}`; return x + y; } const paramGen = parameterizedGenerator(); console.log(paramGen.next()); // { value: 'First yield', done: false } console.log(paramGen.next(10)); // { value: 'Received: 10', done: false } console.log(paramGen.next(20)); // { value: 30, done: true } // Custom iterator const range = { start: 1, end: 5, [Symbol.iterator]() { let current = this.start; const end = this.end; return { next() { if (current <= end) { return { value: current++, done: false }; } else { return { done: true }; } } }; } }; for (const num of range) { console.log(num); // 1, 2, 3, 4, 5 }
Proxy and Reflect
javascript// Basic proxy const target = { name: 'Alice', age: 30 }; const proxy = new Proxy(target, { get(obj, prop) { console.log(`Getting property: ${prop}`); return obj[prop]; }, set(obj, prop, value) { console.log(`Setting ${prop} to ${value}`); obj[prop] = value; return true; } }); console.log(proxy.name); // Getting property: name, then "Alice" proxy.age = 31; // Setting age to 31 // Validation proxy function createValidatedUser(userData) { return new Proxy(userData, { set(obj, prop, value) { if (prop === 'age' && (typeof value !== 'number' || value < 0)) { throw new Error('Age must be a positive number'); } if (prop === 'email' && !value.includes('@')) { throw new Error('Invalid email format'); } obj[prop] = value; return true; } }); } const user = createValidatedUser({ name: 'Bob' }); user.age = 25; // OK // user.age = -5; // Error: Age must be a positive number // Array proxy for negative indexing function createArray(arr) { return new Proxy(arr, { get(target, prop) { if (typeof prop === 'string' && /^-\d+$/.test(prop)) { const index = target.length + parseInt(prop); return target[index]; } return Reflect.get(target, prop); } }); } const arr = createArray([1, 2, 3, 4, 5]); console.log(arr[-1]); // 5 (last element) console.log(arr[-2]); // 4 (second to last)
Modern Features (ES2020+)
javascript// Optional chaining (?.) const user = { name: 'Alice', address: { street: '123 Main St', city: 'New York' } }; console.log(user?.address?.street); // '123 Main St' console.log(user?.address?.zipCode); // undefined (no error) console.log(user?.phone?.number); // undefined (no error) // Method chaining user?.getName?.(); // Won't throw error if getName doesn't exist // Nullish coalescing (??) const config = { timeout: 0, retries: null, debug: false }; const timeout = config.timeout ?? 5000; // 0 (falsy but not nullish) const retries = config.retries ?? 3; // 3 (null is nullish) const debug = config.debug ?? true; // false (falsy but not nullish) // BigInt for large integers const bigNumber = 123456789012345678901234567890n; const anotherBig = BigInt('987654321098765432109876543210'); console.log(bigNumber + anotherBig); // Dynamic imports async function loadModule() { const { default: lodash } = await import('lodash'); return lodash; } // Private class fields class Counter { #count = 0; // Private field increment() { this.#count++; } get value() { return this.#count; } } const counter = new Counter(); counter.increment(); console.log(counter.value); // 1 // console.log(counter.#count); // SyntaxError: Private field '#count' must be declared in an enclosing class // Logical assignment operators let a = null; let b = 0; let c = 'hello'; a ??= 'default'; // a = a ?? 'default' b ||= 10; // b = b || 10 c &&= c.toUpperCase(); // c = c && c.toUpperCase() console.log(a); // 'default' console.log(b); // 10 console.log(c); // 'HELLO'