Chapter 12: Advanced Concepts and Modern Features
Destructuring Deep Dive
// Advanced array destructuring const matrix = [[1, 2], [3, 4], [5, 6]]; const [[a, b], [c, d]] = matrix; console.log(a, b, c, d); // 1 2 3 4 // Swapping variables let x = 1, y = 2; [x, y] = [y, x]; console.log(x, y); // 2 1 // Function parameter destructuring function processUser({ name, age, email = "No email" }) { console.log(`${name} (${age}) - ${email}`); } processUser({ name: "Alice", age: 30 }); // Alice (30) - No email // Mixed destructuring const response = { data: { users: [ { id: 1, name: "Alice" }, { id: 2, name: "Bob" } ] }, status: 200 }; const { data: { users: [firstUser, secondUser] }, status } = response; console.log(firstUser.name, status); // Alice 200
Template Literals and Tagged Templates
// Basic template literals const name = "Alice"; const age = 30; const message = `Hello, my name is ${name} and I'm ${age} years old.`; // Multi-line strings const html = ` <div class="user"> <h2>${name}</h2> <p>Age: ${age}</p> </div> `; // 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 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.
Modules (ES6)
// math.js - exporting export const PI = 3.14159; export const E = 2.71828; export function add(a, b) { return a + b; } export function multiply(a, b) { return a * b; } // Default export export default function subtract(a, b) { return a - b; } // main.js - importing import subtract, { PI, add, multiply as mult } from './math.js'; import * as math from './math.js'; console.log(add(5, 3)); // 8 console.log(mult(5, 3)); // 15 console.log(subtract(5, 3)); // 2 console.log(math.PI); // 3.14159
Symbols and Iterators
// Symbols for unique object keys const SECRET_PROP = Symbol('secret'); const user = { name: "Alice", [SECRET_PROP]: "Hidden value" }; console.log(user[SECRET_PROP]); // "Hidden value" console.log(Object.keys(user)); // ["name"] - symbol keys are hidden // Well-known symbols const customIterable = { data: [1, 2, 3, 4, 5], [Symbol.iterator]() { let index = 0; const data = this.data; return { next() { if (index < data.length) { return { value: data[index++], done: false }; } else { return { done: true }; } } }; } }; // Now it's iterable! for (const value of customIterable) { console.log(value); // 1, 2, 3, 4, 5 }
Generators
// Generator functions 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 console.log(fib.next().value); // 3 // Generator with parameters function* paramGenerator() { const x = yield "Give me a number"; const y = yield `You gave me ${x}, give me another`; return `Sum is ${x + y}`; } const paramGen = paramGenerator(); console.log(paramGen.next()); // { value: "Give me a number", done: false } console.log(paramGen.next(5)); // { value: "You gave me 5, give me another", done: false } console.log(paramGen.next(10)); // { value: "Sum is 15", done: true }
Advanced Symbol Usage
Custom Object Behavior with Symbols
// Symbol.toPrimitive - customize type conversion const temperatureReading = { celsius: 25, [Symbol.toPrimitive](hint) { switch(hint) { case 'number': return this.celsius; case 'string': return `${this.celsius}°C`; default: return this.celsius; } } }; console.log(+temperatureReading); // 25 console.log(`Temperature: ${temperatureReading}`); // "Temperature: 25°C" console.log(temperatureReading + 10); // 35 // Symbol.toStringTag - customize Object.prototype.toString class CustomCollection { get [Symbol.toStringTag]() { return 'MyCustomCollection'; } } const collection = new CustomCollection(); console.log(Object.prototype.toString.call(collection)); // "[object MyCustomCollection]" // Symbol.species - control derived object construction class MyArray extends Array { static get [Symbol.species]() { return Array; // Derived objects will be Arrays, not MyArrays } } const myArr = new MyArray(1, 2, 3); const mapped = myArr.map(x => x * 2); console.log(mapped instanceof MyArray); // false console.log(mapped instanceof Array); // true
Advanced Generator Patterns
Generator Delegation
function* gen1() { yield 1; yield 2; } function* gen2() { yield 3; yield 4; } function* combinedGen() { yield* gen1(); // Delegate to gen1 yield* gen2(); // Delegate to gen2 yield 5; // Own yield } for (const value of combinedGen()) { console.log(value); // 1, 2, 3, 4, 5 } // Two-way communication with generators function* twoWayGenerator() { const name = yield "What's your name?"; const age = yield `Hello ${name}, how old are you?`; yield `${name} is ${age} years old.`; } const conversation = twoWayGenerator(); console.log(conversation.next().value); // "What's your name?" console.log(conversation.next("Alice").value); // "Hello Alice, how old are you?" console.log(conversation.next(30).value); // "Alice is 30 years old."
Async Generators
// Combining async/await with generators async function* asyncDataGenerator() { const urls = [ 'https://api.example.com/data1', 'https://api.example.com/data2', 'https://api.example.com/data3' ]; for (const url of urls) { const response = await fetch(url); const data = await response.json(); yield data; } } // Consuming async generator async function processAsyncData() { for await (const data of asyncDataGenerator()) { console.log('Received data:', data); } }
Meta-programming with Proxy and Reflect
Advanced Proxy Patterns
// Validation proxy function createValidatedObject(target, validators) { return new Proxy(target, { set(obj, prop, value) { if (validators[prop]) { if (!validators[prop](value)) { throw new Error(`Invalid value for ${prop}: ${value}`); } } return Reflect.set(obj, prop, value); } }); } const userValidators = { age: value => typeof value === 'number' && value >= 0 && value <= 150, email: value => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value), name: value => typeof value === 'string' && value.length > 0 }; const user = createValidatedObject({}, userValidators); user.name = "Alice"; // OK user.age = 30; // OK // user.age = -5; // Error! // user.email = "bad"; // Error! // Observable pattern with Proxy function observable(target, onChange) { return new Proxy(target, { set(obj, prop, value) { const oldValue = obj[prop]; const result = Reflect.set(obj, prop, value); if (oldValue !== value) { onChange(prop, oldValue, value); } return result; }, deleteProperty(obj, prop) { const oldValue = obj[prop]; const result = Reflect.deleteProperty(obj, prop); onChange(prop, oldValue, undefined); return result; } }); } const state = observable({ count: 0 }, (prop, oldVal, newVal) => { console.log(`${prop} changed from ${oldVal} to ${newVal}`); }); state.count = 1; // "count changed from 0 to 1" state.name = "Test"; // "name changed from undefined to Test" delete state.name; // "name changed from Test to undefined"
Advanced Module Patterns
Dynamic Module Loading
// Conditional module loading async function loadFeature(featureName) { switch(featureName) { case 'charts': const { ChartModule } = await import('./charts.js'); return new ChartModule(); case 'maps': const { MapModule } = await import('./maps.js'); return new MapModule(); default: throw new Error(`Unknown feature: ${featureName}`); } } // Module namespace pattern // utils.js export * as arrays from './array-utils.js'; export * as strings from './string-utils.js'; export * as dates from './date-utils.js'; // main.js import * as utils from './utils.js'; console.log(utils.arrays.unique([1, 2, 2, 3])); console.log(utils.strings.capitalize('hello'));
Advanced Destructuring Patterns
// Recursive destructuring const nestedData = { user: { profile: { personal: { name: 'Alice', age: 30 }, professional: { title: 'Developer', experience: 5 } } } }; // Deep destructuring with renaming and defaults const { user: { profile: { personal: { name: userName = 'Anonymous' }, professional: { title: jobTitle = 'Unknown' } } } } = nestedData; console.log(userName, jobTitle); // Alice Developer // Function parameter destructuring with rest function processData({ required, optional = 'default', ...rest }) { console.log(required, optional, rest); } processData({ required: 'value', extra1: 'data1', extra2: 'data2' }); // value default { extra1: 'data1', extra2: 'data2' } // Array destructuring with object matching const users = [ { id: 1, name: 'Alice', role: 'admin' }, { id: 2, name: 'Bob', role: 'user' }, { id: 3, name: 'Charlie', role: 'user' } ]; const [admin, ...regularUsers] = users.filter(u => u.role === 'admin') .concat(users.filter(u => u.role !== 'admin')); console.log(admin); // { id: 1, name: 'Alice', role: 'admin' } console.log(regularUsers); // [{ id: 2, ... }, { id: 3, ... }]
Performance and Memory Considerations
// Efficient string concatenation with template literals const parts = ['Hello', 'World', 'from', 'JavaScript']; // ❌ Inefficient let result1 = ''; for (const part of parts) { result1 += part + ' '; } // ✅ Efficient const result2 = parts.join(' '); const result3 = `${parts.join(' ')}!`; // Symbol registry for memory efficiency const sym1 = Symbol.for('app.user.id'); // Global symbol const sym2 = Symbol.for('app.user.id'); // Same symbol console.log(sym1 === sym2); // true // WeakMap for memory-efficient caching const cache = new WeakMap(); function expensiveOperation(obj) { if (cache.has(obj)) { return cache.get(obj); } const result = { // Expensive calculation computed: Object.keys(obj).length * 100 }; cache.set(obj, result); return result; }
Next Chapter: Chapter 13: The Interview Gauntlet - Common Tricks and Gotchas
Previous Chapter: Chapter 11: The Prototype and Inheritance Saga
Table of Contents: JavaScript Guide
