>[!tip] Concrete examples for each concept, showing exactly how CS concepts are applied in web development. This approach should be extremely helpful for CS graduates who understand the theoretical foundations but need to see how they translate to practical web development. >[!caution] "Dirty" examples. Serves as proof of concept. Don't blindly copy or follow if you do not truly understand what every line of code does! # DO NOT USE THIS PAGE. They serve a unique, niche, pedagogic purpose. If you don't know me personally, DON'T use it. >[!caution] They serve as niche references for experienced readers who are aware of bad practices, and just wanted a quick proof of concept. They are not examples of good practices and, upon quick scrutinization, non-ideal practices are being applied. - [[#Factory Pattern|Factory Pattern]] - [[#Singleton Pattern|Singleton Pattern]] - [[#Observer Pattern|Observer Pattern]] - [[#Decorator Pattern|Decorator Pattern]] - [[#Strategy Pattern|Strategy Pattern]] - [[#Dependency Injection|Dependency Injection]] - [[#Proxy Pattern|Proxy Pattern]] - [[#Composition over Inheritance|Composition over Inheritance]] - [[#Pure Functions|Pure Functions]] - [[#Immutability|Immutability]] - [[#Web Fundamentals (The Building Blocks)|Web Fundamentals (The Building Blocks)]] - [[#Web Fundamentals (The Building Blocks)#DOM (Document Object Model) - Tree Data Structure|DOM (Document Object Model) - Tree Data Structure]] - [[#Web Fundamentals (The Building Blocks)#HTML - Markup Language/Structured Document Format|HTML - Markup Language/Structured Document Format]] - [[#Web Fundamentals (The Building Blocks)#CSS - Declarative Rule System|CSS - Declarative Rule System]] - [[#Web Fundamentals (The Building Blocks)#JavaScript - General-purpose Programming Language|JavaScript - General-purpose Programming Language]] - [[#Web Fundamentals (The Building Blocks)#HTTP/HTTPS - Application Layer Protocol|HTTP/HTTPS - Application Layer Protocol]] - [[#DOM Manipulation and Events|DOM Manipulation and Events]] - [[#DOM Manipulation and Events#DOM Manipulation - Tree Modification Algorithms|DOM Manipulation - Tree Modification Algorithms]] - [[#DOM Manipulation and Events#Event Handling - Observer Pattern|Event Handling - Observer Pattern]] - [[#JavaScript Core Concepts|JavaScript Core Concepts]] - [[#JavaScript Core Concepts#Closures - Lexical Scoping/Captured Environment|Closures - Lexical Scoping/Captured Environment]] - [[#JavaScript Core Concepts#Promises - Future/Deferred Computation|Promises - Future/Deferred Computation]] - [[#JavaScript Core Concepts#Async/Await - Syntactic Sugar for Promises/Coroutines|Async/Await - Syntactic Sugar for Promises/Coroutines]] - [[#Web Components & Frameworks|Web Components & Frameworks]] - [[#Web Components & Frameworks#Components - Encapsulated UI Elements|Components - Encapsulated UI Elements]] - [[#Web Components & Frameworks#Virtual DOM - Tree Diffing Algorithm|Virtual DOM - Tree Diffing Algorithm]] - [[#HTTP and Data Fetching|HTTP and Data Fetching]] - [[#HTTP and Data Fetching#REST API - Resource-Oriented Architecture|REST API - Resource-Oriented Architecture]] - [[#HTTP and Data Fetching#GraphQL - Type System + Query Language|GraphQL - Type System + Query Language]] - [[#State Management|State Management]] - [[#State Management#Redux - State Machine with Immutable Updates|Redux - State Machine with Immutable Updates]] - [[#State Management#React Hooks - Function-based State and Effects|React Hooks - Function-based State and Effects]] - [[#Web Security|Web Security]] - [[#Web Security#Cross-Site Scripting (XSS) - Code Injection Attack|Cross-Site Scripting (XSS) - Code Injection Attack]] - [[#Web Security#Content Security Policy (CSP) - Security Policy Language|Content Security Policy (CSP) - Security Policy Language]] - [[#Performance Optimization|Performance Optimization]] - [[#Performance Optimization#Code Splitting - Dynamic Loading|Code Splitting - Dynamic Loading]] - [[#Performance Optimization#Memoization - Caching Function Results|Memoization - Caching Function Results]] - [[#Modern Web Capabilities|Modern Web Capabilities]] - [[#Modern Web Capabilities#Service Workers - Background Processing|Service Workers - Background Processing]] - [[#Modern Web Capabilities#WebAssembly (WASM) - Low-Level Bytecode Format|WebAssembly (WASM) - Low-Level Bytecode Format]] - [[#Testing|Testing]] - [[#Testing#Unit Testing - Function/Module Verification|Unit Testing - Function/Module Verification]] - [[#Backend and Full-Stack Concepts|Backend and Full-Stack Concepts]] - [[#Backend and Full-Stack Concepts#Server-Side Rendering (SSR) - Server-Generated UI|Server-Side Rendering (SSR) - Server-Generated UI]] - [[#Backend and Full-Stack Concepts#ORM (Object-Relational Mapping) - Data Abstraction Layer|ORM (Object-Relational Mapping) - Data Abstraction Layer]] - [[#Backend and Full-Stack Concepts#Microservices - Distributed Systems Architecture|Microservices - Distributed Systems Architecture]] - [[#CSS Concepts|CSS Concepts]] - [[#CSS Concepts#CSS Box Model - Layout Algorithm|CSS Box Model - Layout Algorithm]] - [[#CSS Concepts#Flexbox - Layout Algorithm|Flexbox - Layout Algorithm]] - [[#Advanced JavaScript|Advanced JavaScript]] - [[#Advanced JavaScript#Promises and Async Programming - Concurrency Model|Promises and Async Programming - Concurrency Model]] - [[#Advanced JavaScript#Progressive Web Apps (PWAs) - Hybrid Application Model|Progressive Web Apps (PWAs) - Hybrid Application Model]] - [[#Advanced JavaScript#OAuth 2.0 - Delegated Authorization Protocol|OAuth 2.0 - Delegated Authorization Protocol]] - [[#Data Structures in Web Development|Data Structures in Web Development]] - [[#Data Structures in Web Development#Trie for Autocomplete - Prefix Tree Implementation|Trie for Autocomplete - Prefix Tree Implementation]] - [[#Web Animation and Graphics|Web Animation and Graphics]] - [[#Web Animation and Graphics#Canvas API - Immediate Mode Rendering|Canvas API - Immediate Mode Rendering]] - [[#Web Workers and Multithreading|Web Workers and Multithreading]] - [[#Web Workers and Multithreading#Web Workers - Browser Multithreading|Web Workers - Browser Multithreading]] - [[#Browser Storage - IndexedDB(Warning - dirty - proof of concept)|Browser Storage - IndexedDB(Warning - dirty - proof of concept)]] - [[#Browser Storage - IndexedDB(Warning - dirty - proof of concept)#IndexedDB - Client-Side Database|IndexedDB - Client-Side Database]] ## Factory Pattern The Factory Pattern abstracts object creation logic, allowing creation of objects without specifying their exact class. ```javascript // ===== Factory Pattern Example ===== // A simple UI component factory that centralizes component creation class ComponentFactory { // The factory method decides which component to create based on type createComponent(type, props) { // This centralized creation logic allows us to: // 1. Apply common modifications to all components // 2. Select different implementations based on configuration // 3. Hide complex initialization logic from consumers // Apply common props to all components const commonProps = { className: `ui-component ${props.className || ''}`, 'data-testid': `${type}-component`, ...props }; // Select the appropriate component based on type switch (type) { case 'button': // We could return different button implementations based on // feature flags, user preferences, or platform detection if (props.variant === 'primary') { return new PrimaryButton(commonProps); } else { return new SecondaryButton(commonProps); } case 'input': // Complex initialization logic is hidden from the consumer const input = new TextInput(commonProps); input.setupValidation(); input.attachMask(); return input; case 'card': return new Card(commonProps); default: throw new Error(`Unknown component type: ${type}`); } } } // Component implementations class PrimaryButton { constructor(props) { this.props = props; this.element = document.createElement('button'); this.element.className = `${props.className} btn-primary`; this.element.textContent = props.text || 'Button'; } render() { return this.element; } } class SecondaryButton { constructor(props) { this.props = props; this.element = document.createElement('button'); this.element.className = `${props.className} btn-secondary`; this.element.textContent = props.text || 'Button'; } render() { return this.element; } } class TextInput { constructor(props) { this.props = props; this.element = document.createElement('input'); this.element.className = props.className; this.element.type = 'text'; this.element.placeholder = props.placeholder || ''; } setupValidation() { // Complex validation setup hidden from consumers const pattern = this.props.pattern || '.*'; this.element.pattern = pattern; } attachMask() { // Apply input masks if needed if (this.props.mask) { // Implementation of input masking } } render() { return this.element; } } class Card { constructor(props) { this.props = props; this.element = document.createElement('div'); this.element.className = `${props.className} card`; } render() { return this.element; } } // Usage example const factory = new ComponentFactory(); // Client code doesn't need to know about specific implementations const button = factory.createComponent('button', { text: 'Click Me', variant: 'primary', onClick: () => console.log('Button clicked!') }); const input = factory.createComponent('input', { placeholder: 'Enter your name', pattern: '[A-Za-z]+' }); document.body.appendChild(button.render()); document.body.appendChild(input.render()); // In React, React.createElement is a factory function: /* const element = React.createElement( 'div', { className: 'container' }, 'Hello, world!' ); */ // In Angular, dependency injection uses factories to create services: /* @Injectable({ providedIn: 'root', factory: () => { return environment.production ? new ProductionAuthService() : new MockAuthService(); } }) class AuthService { } */ ``` ## Singleton Pattern The Singleton Pattern ensures a class has only one instance with global access to that instance. ```javascript // ===== Singleton Pattern Example ===== // A store singleton that manages application state class Store { constructor() { // Private state container this._state = { user: null, products: [], cart: [], theme: 'light', notifications: [] }; // Keep track of subscribers this._subscribers = []; // Check if we already have an instance if (Store.instance) { // If so, return the existing instance return Store.instance; } // Otherwise save this instance Store.instance = this; } // Get the state (or a slice of it) getState(path) { if (!path) { // Return a copy to prevent direct mutations return JSON.parse(JSON.stringify(this._state)); } // Get a specific part of the state const keys = path.split('.'); let result = this._state; for (const key of keys) { if (result[key] === undefined) { return undefined; } result = result[key]; } return JSON.parse(JSON.stringify(result)); } // Update the state setState(updater) { // Create a new state object (immutability principle) const nextState = typeof updater === 'function' ? updater(this._state) : { ...this._state, ...updater }; // Update the state this._state = nextState; // Notify subscribers this._subscribers.forEach(callback => callback(this._state)); } // Subscribe to state changes subscribe(callback) { this._subscribers.push(callback); // Return an unsubscribe function return () => { this._subscribers = this._subscribers.filter(cb => cb !== callback); }; } } // Usage example // First creation of the store const store = new Store(); // This will return the same instance const sameStore = new Store(); console.log(store === sameStore); // true - it's the same instance // Using the store throughout the application store.setState({ theme: 'dark' }); // In a different part of the app const currentTheme = store.getState('theme'); console.log(currentTheme); // 'dark' // Subscribe to changes const unsubscribe = store.subscribe(state => { console.log('State updated:', state); // Update UI based on state changes }); // Later when no longer needed unsubscribe(); // In React, Redux uses a singleton store: /* const store = createStore(reducer); // This same store is used throughout the app <Provider store={store}> <App /> </Provider> */ // In Angular, services are often singletons: /* @Injectable({ providedIn: 'root' // This makes it a singleton }) export class AuthService { // ... } */ ``` ## Observer Pattern The Observer Pattern defines a one-to-many dependency where when one object changes state, all its dependents are notified. ```javascript // ===== Observer Pattern Example ===== // A simple implementation of the observer pattern class Observable { constructor() { // Array to store observers (subscribers) this.observers = []; } // Add an observer to the list subscribe(observerFunction) { // Make sure we don't add duplicates if (!this.observers.includes(observerFunction)) { this.observers.push(observerFunction); } // Return an unsubscribe function for cleanup return { unsubscribe: () => { this.observers = this.observers.filter(obs => obs !== observerFunction); } }; } // Remove an observer from the list unsubscribe(observerFunction) { this.observers = this.observers.filter(obs => obs !== observerFunction); } // Notify all observers with data notify(data) { // If we have observers, call each one with the data if (this.observers.length > 0) { this.observers.forEach(observer => observer(data)); } } } // A FormControl class that uses the observer pattern class FormControl extends Observable { constructor(initialValue = '') { // Initialize the Observable parent super(); // Internal state this._value = initialValue; this._errors = null; this._touched = false; this._validators = []; } // Get current value get value() { return this._value; } // Update value and notify observers set value(newValue) { // Update the internal value this._value = newValue; // Run validation this.validate(); // Notify observers about the change this.notify({ value: this._value, errors: this._errors, touched: this._touched, valid: this.valid }); } // Get validation status get valid() { return this._errors === null; } // Get errors get errors() { return this._errors; } // Mark as touched (e.g., after focus leaves the field) markAsTouched() { this._touched = true; // Notify observers this.notify({ value: this._value, errors: this._errors, touched: this._touched, valid: this.valid }); } // Add validators setValidators(validators) { if (Array.isArray(validators)) { this._validators = validators; } else { this._validators = [validators]; } // Re-run validation with new validators this.validate(); } // Run all validators validate() { this._errors = null; for (const validator of this._validators) { const error = validator(this._value); if (error) { this._errors = { ...this._errors, ...error }; } } } } // Example validators const required = value => { return value === null || value === undefined || value === '' ? { required: 'This field is required' } : null; }; const minLength = min => { return value => { return !value || value.length < min ? { minlength: `Minimum length is ${min}` } : null; }; }; // Usage example const usernameControl = new FormControl(''); usernameControl.setValidators([required, minLength(5)]); // Subscribe to changes const subscription = usernameControl.subscribe(state => { console.log('Form control changed:', state); // Update UI based on the state if (state.touched && !state.valid) { // Show error message console.log('Errors:', state.errors); } }); // User input simulation usernameControl.value = 'abc'; // Too short, will trigger validation error usernameControl.markAsTouched(); // Later when not needed anymore subscription.unsubscribe(); // In Angular, FormControls use the Observer pattern: /* const nameControl = new FormControl('', [ Validators.required, Validators.minLength(4) ]); nameControl.valueChanges.subscribe(value => { console.log('Name changed:', value); }); */ // React's useState with useEffect can create observer-like patterns: /* const [name, setName] = useState(''); // This effect "observes" changes to name useEffect(() => { console.log('Name changed:', name); // Validation if (name.length < 5) { setNameError('Name is too short'); } else { setNameError(null); } }, [name]); */ ``` ## Decorator Pattern The Decorator Pattern attaches additional responsibilities to objects dynamically, providing a flexible alternative to subclassing. ```javascript // ===== Decorator Pattern Example ===== // Base component interface class Component { render() { throw new Error('Component subclasses must implement render()'); } } // Concrete component class Button extends Component { constructor(text) { super(); this.text = text; } render() { return `<button>${this.text}</button>`; } } // Decorator base class class ComponentDecorator extends Component { constructor(component) { super(); // The component we're decorating this.component = component; } // Default behavior delegates to decorated component render() { return this.component.render(); } } // Specific decorators class DisabledDecorator extends ComponentDecorator { render() { // Get the basic rendering from the wrapped component const renderedComponent = this.component.render(); // Add the disabled attribute to the output return renderedComponent.replace('<button', '<button disabled'); } } class ClassDecorator extends ComponentDecorator { constructor(component, className) { super(component); this.className = className; } render() { const renderedComponent = this.component.render(); // Add the class to the output return renderedComponent.replace('<button', `<button class="${this.className}"`); } } class TooltipDecorator extends ComponentDecorator { constructor(component, tooltipText) { super(component); this.tooltipText = tooltipText; } render() { const renderedComponent = this.component.render(); // Add the tooltip attributes return renderedComponent.replace('<button', `<button title="${this.tooltipText}" data-tooltip="${this.tooltipText}"`); } } class EventDecorator extends ComponentDecorator { constructor(component, eventType, eventHandler) { super(component); this.eventType = eventType; this.eventHandler = eventHandler; } render() { const renderedComponent = this.component.render(); // This is simplified - in a real implementation, we would // actually attach the event handler, not just add an attribute return renderedComponent.replace('<button', `<button data-event="${this.eventType}"`); } getEventHandler() { return this.eventHandler; } } // Usage example let submitButton = new Button('Submit'); console.log(submitButton.render()); // <button>Submit</button> // Wrap it with decorators submitButton = new ClassDecorator(submitButton, 'btn btn-primary'); submitButton = new TooltipDecorator(submitButton, 'Submit the form'); submitButton = new EventDecorator(submitButton, 'click', () => { console.log('Button clicked!'); }); // Conditionally add more decorators if (formIsInvalid) { submitButton = new DisabledDecorator(submitButton); } // The final rendered output with all decorations applied console.log(submitButton.render()); // <button class="btn btn-primary" title="Submit the form" data-tooltip="Submit the form" data-event="click" disabled>Submit</button> // Higher-Order Components in React are decorators: /* // A HOC that adds loading functionality function withLoading(Component) { return function WithLoadingComponent({ isLoading, ...props }) { if (isLoading) { return <div>Loading...</div>; } return <Component {...props} />; }; } // Using the HOC to decorate a component const ButtonWithLoading = withLoading(Button); // Using the decorated component <ButtonWithLoading isLoading={true} onClick={handleClick}> Click Me </ButtonWithLoading> */ // In TypeScript/JavaScript, decorators are built into the language: /* function log(target, key, descriptor) { const original = descriptor.value; descriptor.value = function(...args) { console.log(`Calling ${key} with arguments: ${args}`); return original.apply(this, args); }; return descriptor; } class Calculator { @log add(a, b) { return a + b; } } */ ``` ## Strategy Pattern The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. ```javascript // ===== Strategy Pattern Example ===== // Strategy interface (implicit in JavaScript) // In a strictly typed language, we'd define an interface like: // interface ValidationStrategy { // validate(value: string): ValidationResult; // } // Concrete validation strategies class RequiredFieldValidator { validate(value) { return value !== null && value !== undefined && value !== '' ? { valid: true } : { valid: false, error: 'This field is required' }; } } class EmailValidator { validate(value) { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(value) ? { valid: true } : { valid: false, error: 'Please enter a valid email address' }; } } class MinLengthValidator { constructor(minLength) { this.minLength = minLength; } validate(value) { return value && value.length >= this.minLength ? { valid: true } : { valid: false, error: `Must be at least ${this.minLength} characters` }; } } class PasswordStrengthValidator { validate(value) { // Check for at least 8 chars, one uppercase, one lowercase, one number const strongPasswordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/; return strongPasswordRegex.test(value) ? { valid: true } : { valid: false, error: 'Password must have at least 8 characters, including uppercase, lowercase, and numbers' }; } } // Context class that uses the strategies class FormField { constructor(name, value = '') { this.name = name; this.value = value; this.validators = []; this.errors = []; } // Set the validation strategies for this field setValidators(validators) { // We can dynamically set and change validation strategies this.validators = validators; } // Validate using all assigned strategies validate() { this.errors = []; // Run each validation strategy for (const validator of this.validators) { const result = validator.validate(this.value); if (!result.valid) { this.errors.push(result.error); } } return this.errors.length === 0; } // Update the value setValue(newValue) { this.value = newValue; return this; } // Get validation errors getErrors() { return this.errors; } } // Usage example const emailField = new FormField('email', ''); // Set validation strategies emailField.setValidators([ new RequiredFieldValidator(), new EmailValidator() ]); // Test validation emailField.setValue('').validate(); console.log(emailField.getErrors()); // ['This field is required', 'Please enter a valid email address'] emailField.setValue('invalid').validate(); console.log(emailField.getErrors()); // ['Please enter a valid email address'] emailField.setValue('[email protected]').validate(); console.log(emailField.getErrors()); // [] // Password field with different validation strategies const passwordField = new FormField('password'); passwordField.setValidators([ new RequiredFieldValidator(), new MinLengthValidator(8), new PasswordStrengthValidator() ]); // Change strategies based on requirements if (isSignupForm) { // Add password strength validation for new users passwordField.setValidators([ ...passwordField.validators, new PasswordStrengthValidator() ]); } // In React, render props pattern implements the strategy pattern: /* // A component that uses different rendering strategies <DataFetcher url="/api/users" render={data => ( <UserList users={data} /> )} /> <DataFetcher url="/api/products" render={data => ( <ProductGrid products={data} /> )} /> */ // In Angular, strategies can be injected: /* @Injectable() class LoggingService { constructor(@Inject(LOGGING_STRATEGY) private strategy: LoggingStrategy) {} log(message: string): void { this.strategy.log(message); } } // Different strategies @Injectable() class ConsoleLoggingStrategy implements LoggingStrategy { log(message: string): void { console.log(message); } } @Injectable() class ServerLoggingStrategy implements LoggingStrategy { constructor(private http: HttpClient) {} log(message: string): void { this.http.post('/api/logs', { message }).subscribe(); } } */ ``` ## Dependency Injection Dependency Injection is a technique where an object receives other objects it depends on, rather than creating them itself. ```javascript // ===== Dependency Injection Example ===== // Service interfaces (implicit in JavaScript) // interface AuthService { // login(username: string, password: string): Promise<User>; // logout(): void; // getCurrentUser(): User | null; // } // Concrete service implementations class HttpAuthService { constructor(httpClient, apiUrl) { this.httpClient = httpClient; this.apiUrl = apiUrl; this.currentUser = null; } async login(username, password) { try { const response = await this.httpClient.post(`${this.apiUrl}/auth/login`, { username, password }); this.currentUser = response.data.user; localStorage.setItem('token', response.data.token); return this.currentUser; } catch (error) { console.error('Login failed:', error); throw new Error('Authentication failed'); } } logout() { this.currentUser = null; localStorage.removeItem('token'); } getCurrentUser() { return this.currentUser; } } class MockAuthService { constructor() { this.currentUser = null; // Mock user database for testing this.users = [ { id: 1, username: 'testuser', password: 'password', name: 'Test User' } ]; } async login(username, password) { // Simulate network delay await new Promise(resolve => setTimeout(resolve, 500)); const user = this.users.find( u => u.username === username && u.password === password ); if (user) { // Clone to avoid exposing password this.currentUser = { id: user.id, username: user.username, name: user.name }; return this.currentUser; } else { throw new Error('Invalid credentials'); } } logout() { this.currentUser = null; } getCurrentUser() { return this.currentUser; } } // HTTP client implementation class HttpClient { async get(url) { // Real implementation would use fetch or XMLHttpRequest return fetch(url).then(response => response.json()); } async post(url, data) { // Real implementation would use fetch or XMLHttpRequest return fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }).then(response => response.json()); } } // Simple dependency container/injector class DependencyContainer { constructor() { this.services = new Map(); this.factories = new Map(); } // Register a service instance register(key, instance) { this.services.set(key, instance); } // Register a factory function that will create the service registerFactory(key, factory) { this.factories.set(key, factory); } // Get a service (creating it via factory if needed) get(key) { if (this.services.has(key)) { return this.services.get(key); } if (this.factories.has(key)) { const factory = this.factories.get(key); const instance = factory(this); this.services.set(key, instance); return instance; } throw new Error(`Service not found: ${key}`); } } // LoginComponent with injected dependencies class LoginComponent { // Dependencies are injected through the constructor constructor(authService, router) { this.authService = authService; this.router = router; this.username = ''; this.password = ''; this.error = null; } // Handle the login form submission async handleLogin(username, password) { try { this.error = null; await this.authService.login(username, password); this.router.navigate('/dashboard'); } catch (error) { this.error = error.message; } } render() { // Render the login form return ` <form id="login-form"> <h2>Login</h2> ${this.error ? `<div class="error">${this.error}</div>` : ''} <div> <label for="username">Username:</label> <input id="username" value="${this.username}" /> </div> <div> <label for="password">Password:</label> <input id="password" type="password" value="${this.password}" /> </div> <button type="submit">Login</button> </form> `; } } // Router service class Router { navigate(path) { console.log(`Navigating to ${path}`); // In a real app, this would update the URL and trigger route matching window.history.pushState({}, '', path); } } // Usage example - setting up the DI container const container = new DependencyContainer(); // Register services based on environment if (process.env.NODE_ENV === 'production') { // Register production dependencies container.register('httpClient', new HttpClient()); container.registerFactory('authService', container => { const httpClient = container.get('httpClient'); return new HttpAuthService(httpClient, 'https://api.example.com'); }); } else { // Register testing/development dependencies container.register('authService', new MockAuthService()); } // Register other services container.register('router', new Router()); // Create component with injected dependencies const loginComponent = new LoginComponent( container.get('authService'), container.get('router') ); // Attach component to the DOM document.getElementById('app').innerHTML = loginComponent.render(); // In Angular, DI is built into the framework: /* @Injectable({ providedIn: 'root' }) class AuthService { constructor(private http: HttpClient) {} login(username: string, password: string): Observable<User> { return this.http.post<User>('/api/login', { username, password }); } } @Component({ selector: 'app-login', template: `<form (submit)="login()">...</form>` }) class LoginComponent { // Dependencies automatically injected constructor(private authService: AuthService, private router: Router) {} login() { this.authService.login(this.username, this.password) .subscribe(() => this.router.navigate(['/dashboard'])); } } */ // In React, context API provides a form of dependency injection: /* // Create a context for auth service const AuthContext = React.createContext(null); // Provider component function AuthProvider({ children }) { const authService = new HttpAuthService(httpClient, '/api'); return ( <AuthContext.Provider value={authService}> {children} </AuthContext.Provider> ); } // Component consuming the injected dependency function LoginButton() { // Get the dependency from context const authService = useContext(AuthContext); return ( <button onClick={() => authService.login()}> Login </button> ); } */ ``` ## Proxy Pattern The Proxy Pattern provides a surrogate or placeholder for another object to control access to it. ```javascript // ===== Proxy Pattern Example ===== // Subject interface (the real object's interface) class UserAPI { async getUsers() { // Implementation would fetch users from server console.log('Fetching users from API'); // Simulate API call return new Promise(resolve => { setTimeout(() => { resolve([ { id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }, { id: 3, name: 'Charlie' } ]); }, 300); }); } async getUserById(id) { console.log(`Fetching user with ID ${id} from API`); // Simulate API call return new Promise(resolve => { setTimeout(() => { resolve({ id, name: ['Alice', 'Bob', 'Charlie'][id - 1] }); }, 300); }); } } // Proxy that adds caching class CachingUserAPIProxy { constructor(realUserAPI) { this.realUserAPI = realUserAPI; this.cache = new Map(); } async getUsers() { const cacheKey = 'all_users'; // Check if we have a cached value if (this.cache.has(cacheKey)) { console.log('Returning cached users'); return this.cache.get(cacheKey); } // Otherwise call the real API const users = await this.realUserAPI.getUsers(); // Cache the result this.cache.set(cacheKey, users); return users; } async getUserById(id) { const cacheKey = `user_${id}`; // Check if we have a cached value if (this.cache.has(cacheKey)) { console.log(`Returning cached user with ID ${id}`); return this.cache.get(cacheKey); } // Otherwise call the real API const user = await this.realUserAPI.getUserById(id); // Cache the result this.cache.set(cacheKey, user); return user; } // Method to clear the cache (not in the original subject) clearCache() { console.log('Clearing cache'); this.cache.clear(); } // Method to invalidate specific cache entry invalidateUser(id) { console.log(`Invalidating cache for user ${id}`); this.cache.delete(`user_${id}`); // Also invalidate the full list cache since it might contain this user this.cache.delete('all_users'); } } // Another proxy for access control class AuthenticatedUserAPIProxy { constructor(realUserAPI, authService) { this.realUserAPI = realUserAPI; this.authService = authService; } async getUsers() { // Check if user is authenticated if (!this.authService.isAuthenticated()) { throw new Error('Authentication required'); } // Check if user has admin role if (!this.authService.hasRole('admin')) { throw new Error('Admin access required'); } // If authenticated and authorized, forward to real object return this.realUserAPI.getUsers(); } async getUserById(id) { // Check if user is authenticated if (!this.authService.isAuthenticated()) { throw new Error('Authentication required'); } // Get current user ID const currentUserId = this.authService.getCurrentUserId(); // Users can only access their own data unless they're admins if (id !== currentUserId && !this.authService.hasRole('admin')) { throw new Error('Access denied'); } // If authorized, forward to real object return this.realUserAPI.getUserById(id); } } // Usage example async function demonstrateProxy() { // Create the real subject const realAPI = new UserAPI(); // Create a proxy const cachedAPI = new CachingUserAPIProxy(realAPI); // First call - should hit the real API console.log('First call:'); const users1 = await cachedAPI.getUsers(); console.log(users1); // Second call - should use the cache console.log('\nSecond call:'); const users2 = await cachedAPI.getUsers(); console.log(users2); // Get a specific user - hits the API console.log('\nGetting user 1:'); const user1 = await cachedAPI.getUserById(1); console.log(user1); // Get the same user again - uses cache console.log('\nGetting user 1 again:'); const user1Again = await cachedAPI.getUserById(1); console.log(user1Again); // Clear the cache console.log('\nClearing cache'); cachedAPI.clearCache(); // This call will hit the API again console.log('\nAfter clearing cache:'); const users3 = await cachedAPI.getUsers(); console.log(users3); } // demonstrateProxy(); // JavaScript's built-in Proxy object: /* const handler = { get(target, prop, receiver) { console.log(`Getting property ${prop}`); return Reflect.get(target, prop, receiver); }, set(target, prop, value, receiver) { console.log(`Setting property ${prop} to ${value}`); return Reflect.set(target, prop, value, receiver); } }; const user = { name: 'Alice', age: 30 }; const proxiedUser = new Proxy(user, handler); // Logs: "Getting property name" console.log(proxiedUser.name); // Logs: "Setting property age to 31" proxiedUser.age = 31; */ // Vue.js uses Proxies for its reactivity system: /* // Simplified version of Vue's reactivity function reactive(obj) { return new Proxy(obj, { get(target, key, receiver) { // Track that this property was accessed track(target, key); return Reflect.get(target, key, receiver); }, set(target, key, value, receiver) { const oldValue = target[key]; const result = Reflect.set(target, key, value, receiver); if (oldValue !== value) { // Trigger updates when property changes trigger(target, key); } return result; } }); } const state = reactive({ count: 0 }); // When state.count changes, UI automatically updates */ ``` ## Composition over Inheritance Composition over Inheritance is a principle that suggests favoring object composition over class inheritance to achieve polymorphic behavior and code reuse. ```javascript // ===== Composition over Inheritance Example ===== // Bad example: Using inheritance for behavior extension // Base animal class class Animal { constructor(name) { this.name = name; } eat() { return `${this.name} is eating.`; } sleep() { return `${this.name} is sleeping.`; } } // Derived classes class Bird extends Animal { fly() { return `${this.name} is flying.`; } } class Fish extends Animal { swim() { return `${this.name} is swimming.`; } } // But what about a flying fish? Or a penguin (bird that swims but doesn't fly)? // We end up with complex inheritance hierarchies: class FlyingFish extends Fish { fly() { return `${this.name} is flying.`; } } class Penguin extends Bird { // Override to throw an error or provide different behavior fly() { throw new Error("Penguins can't fly!"); } swim() { return `${this.name} is swimming.`; } } // Better approach: Using composition // Start with small, focused behavior classes class EatingBehavior { constructor(name) { this.name = name; } eat() { return `${this.name} is eating.`; } } class SleepingBehavior { constructor(name) { this.name = name; } sleep() { return `${this.name} is sleeping.`; } } class FlyingBehavior { constructor(name) { this.name = name; } fly() { return `${this.name} is flying.`; } } class SwimmingBehavior { constructor(name) { this.name = name; } swim() { return `${this.name} is swimming.`; } } // Now create animals by composing behaviors class AnimalWithComposition { constructor(name) { this.name = name; this.behaviors = []; } // Add behaviors dynamically addBehavior(behavior) { this.behaviors.push(behavior); } // Method missing implementation - forwards to the appropriate behavior getBehaviorMethod(method) { const behavior = this.behaviors.find(b => typeof b[method] === 'function'); if (behavior) { return behavior[method].bind(behavior); } return null; } } // Create a proxy that forwards method calls to behaviors const createAnimal = (name, ...behaviors) => { const animal = new AnimalWithComposition(name); // Add the behaviors, passing the name to each behaviors.forEach(BehaviorClass => { animal.addBehavior(new BehaviorClass(name)); }); // Return a proxy that forwards method calls to behaviors return new Proxy(animal, { get(target, prop) { // If the property exists on the animal, return it if (prop in target) { return target[prop]; } // Try to find a behavior with this method const method = target.getBehaviorMethod(prop); if (method) { return method; } // Method not found return undefined; } }); }; // Usage examples const eagle = createAnimal( 'Eagle', EatingBehavior, SleepingBehavior, FlyingBehavior ); console.log(eagle.eat()); // Eagle is eating. console.log(eagle.sleep()); // Eagle is sleeping. console.log(eagle.fly()); // Eagle is flying. // A penguin can swim but not fly const penguin = createAnimal( 'Penguin', EatingBehavior, SleepingBehavior, SwimmingBehavior ); console.log(penguin.eat()); // Penguin is eating. console.log(penguin.swim()); // Penguin is swimming. console.log(penguin.fly); // undefined (penguins can't fly) // A flying fish can both swim and fly const flyingFish = createAnimal( 'Flying Fish', EatingBehavior, SleepingBehavior, SwimmingBehavior, FlyingBehavior ); console.log(flyingFish.swim()); // Flying Fish is swimming. console.log(flyingFish.fly()); // Flying Fish is flying. // We can even add behaviors at runtime const duck = createAnimal( 'Duck', EatingBehavior, SleepingBehavior ); // Later add more behaviors duck.addBehavior(new SwimmingBehavior(duck.name)); duck.addBehavior(new FlyingBehavior(duck.name)); console.log(duck.swim()); // Duck is swimming. console.log(duck.fly()); // Duck is flying. // In React, composition is preferred over inheritance: /* // Generic modal component function Modal(props) { return ( <div className="modal"> <div className="modal-content"> {props.children} </div> </div> ); } // Generic button component function Button(props) { return ( <button className={`btn ${props.className || ''}`} onClick={props.onClick}> {props.children} </button> ); } // Instead of extending these components, compose them: function ConfirmationDialog(props) { return ( <Modal> <h2>{props.title}</h2> <p>{props.message}</p> <div className="actions"> <Button onClick={props.onCancel}>Cancel</Button> <Button className="primary" onClick={props.onConfirm}>Confirm</Button> </div> </Modal> ); } */ ``` ## Pure Functions Pure functions are functions whose output depends only on their inputs and have no side effects. ```javascript // ===== Pure Functions Example ===== // Impure function - has side effects let total = 0; function addToTotal(value) { total += value; // Side effect: modifies external state return total; } console.log(addToTotal(5)); // 5 console.log(addToTotal(5)); // 10 - different result with same input! // Pure function - returns the same output for the same input, no side effects function add(a, b) { return a + b; } console.log(add(5, 3)); // 8 console.log(add(5, 3)); // 8 - always the same output for the same input // Another impure function - depends on external state let exchangeRate = 1.2; function convertToUSD(euros) { return euros * exchangeRate; // Depends on external variable } console.log(convertToUSD(100)); // 120 // Change external state exchangeRate = 1.3; console.log(convertToUSD(100)); // 130 - different result for the same input! // Pure version of the same function function convertToUSD_pure(euros, rate) { return euros * rate; } console.log(convertToUSD_pure(100, 1.2)); // 120 console.log(convertToUSD_pure(100, 1.2)); // 120 - always the same // Impure function - has side effect (alters input) function addItemToCart(cart, item) { cart.push(item); // Modifies the input array return cart; } const cart = ['apple']; addItemToCart(cart, 'orange'); console.log(cart); // ['apple', 'orange'] - original array was modified! // Pure version of the same function function addItemToCart_pure(cart, item) { return [...cart, item]; // Returns a new array, doesn't modify input } const cart2 = ['apple']; const newCart = addItemToCart_pure(cart2, 'orange'); console.log(cart2); // ['apple'] - original is untouched console.log(newCart); // ['apple', 'orange'] - new array is returned // Impure function - side effect (I/O operation) function saveUser(user) { // Side effect: I/O operation localStorage.setItem('user', JSON.stringify(user)); return user; } // More complex pure function function calculateTotalPrice(items, taxRate) { // Reduce is a functional programming pattern - no side effects const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0); const tax = subtotal * taxRate; return { subtotal, tax, total: subtotal + tax }; } const items = [ { name: 'Item 1', price: 10, quantity: 2 }, { name: 'Item 2', price: 15, quantity: 1 }, { name: 'Item 3', price: 5, quantity: 3 } ]; console.log(calculateTotalPrice(items, 0.1)); // { subtotal: 35, tax: 3.5, total: 38.5 } // Benefits of pure functions: // 1. Predictable - same input always gives same output // 2. Testable - no setup or mocking required // 3. Cacheable - results can be memoized // 4. Parallelizable - can run in parallel since no shared state // 5. No race conditions - thread-safe // Example of memoization for pure functions function memoize(fn) { const cache = new Map(); return function(...args) { // Create a key by stringifying the arguments const key = JSON.stringify(args); // If we've seen these arguments before, return cached result if (cache.has(key)) { console.log('Cache hit'); return cache.get(key); } // Otherwise call the function and cache the result console.log('Cache miss'); const result = fn(...args); cache.set(key, result); return result; }; } // Memoize a pure function const memoizedCalculate = memoize(calculateTotalPrice); console.log(memoizedCalculate(items, 0.1)); // Cache miss console.log(memoizedCalculate(items, 0.1)); // Cache hit - same result without recalculation // Redux reducers must be pure functions: /* function counterReducer(state = 0, action) { switch (action.type) { case 'INCREMENT': return state + 1; // Returns new state, doesn't modify existing case 'DECREMENT': return state - 1; // Returns new state, doesn't modify existing default: return state; } } */ // React components should be pure with respect to their props: /* // Pure function component function Greeting({ name }) { return <h1>Hello, {name}</h1>; } // Same input always gives same output <Greeting name="Alice" /> // Always renders <h1>Hello, Alice</h1> */ ``` ## Immutability Immutability is the concept that once created, objects cannot be changed, and operations on them create new objects. ```javascript // ===== Immutability Example ===== // Mutable approach let mutableArray = [1, 2, 3]; console.log('Original array:', mutableArray); // Modify array by adding a value mutableArray.push(4); console.log('After push:', mutableArray); // [1, 2, 3, 4] - original array is changed // Modify array by removing a value mutableArray.splice(1, 1); console.log('After splice:', mutableArray); // [1, 3, 4] - original array is changed // Immutable approach const immutableArray = [1, 2, 3]; console.log('Original immutable array:', immutableArray); // Add a value by creating a new array const newArrayWithFour = [...immutableArray, 4]; console.log('Original after add:', immutableArray); // [1, 2, 3] - unchanged console.log('New array after add:', newArrayWithFour); // [1, 2, 3, 4] // Remove a value by creating a new array const newArrayWithout2 = immutableArray.filter(num => num !== 2); console.log('Original after remove:', immutableArray); // [1, 2, 3] - unchanged console.log('New array after remove:', newArrayWithout2); // [1, 3] // Update a value by creating a new array const newArrayWith5 = immutableArray.map(num => num === 2 ? 5 : num); console.log('Original after update:', immutableArray); // [1, 2, 3] - unchanged console.log('New array after update:', newArrayWith5); // [1, 5, 3] // Immutable objects const user = { name: 'Alice', age: 30, email: '[email protected]', address: { city: 'New York', country: 'USA' } }; console.log('Original user:', user); // Mutable update (BAD) user.age = 31; user.address.city = 'Boston'; console.log('After mutable update:', user); // Object is changed // Create a fresh user to work with const immutableUser = { name: 'Alice', age: 30, email: '[email protected]', address: { city: 'New York', country: 'USA' } }; // Immutable update - shallow copy with spread operator const updatedUser = { ...immutableUser, age: 31, // Only changing this property }; console.log('Original after update:', immutableUser); // Unchanged console.log('New object after update:', updatedUser); // Has updated age // Nested immutable update const userWithNewAddress = { ...immutableUser, address: { ...immutableUser.address, city: 'Boston' // Only changing this nested property } }; console.log('Original address:', immutableUser.address); // Unchanged console.log('New address:', userWithNewAddress.address); // Has updated city // Adding a new property immutably const userWithPhone = { ...immutableUser, phone: '555-1234' }; console.log('User with phone:', userWithPhone); // Using Object.freeze for enforced immutability const frozenUser = Object.freeze({ name: 'Bob', age: 25, preferences: { theme: 'dark' } }); // This will fail in strict mode or generate a silent error try { frozenUser.age = 26; } catch (e) { console.log('Cannot modify frozen object'); } console.log('Age after attempted update:', frozenUser.age); // Still 25 // Note: freeze is shallow, nested objects can still be modified try { frozenUser.preferences.theme = 'light'; console.log('Theme was updated:', frozenUser.preferences.theme); // 'light' } catch (e) { console.log('Cannot modify nested object'); } // Deep freeze function function deepFreeze(obj) { // Freeze properties before freezing self Object.getOwnPropertyNames(obj).forEach(prop => { const value = obj[prop]; // Freeze nested objects recursively if (value && typeof value === 'object') { deepFreeze(value); } }); return Object.freeze(obj); } const deepFrozenUser = deepFreeze({ name: 'Carol', age: 35, preferences: { theme: 'dark' } }); // Now nested changes will also fail try { deepFrozenUser.preferences.theme = 'light'; } catch (e) { console.log('Cannot modify deeply frozen object'); } console.log('Theme after attempted update:', deepFrozenUser.preferences.theme); // Still 'dark' // Libraries like Immutable.js provide efficient immutable data structures /* import { Map } from 'immutable'; const map1 = Map({ a: 1, b: 2, c: 3 }); const map2 = map1.set('b', 50); map1.get('b'); // 2 map2.get('b'); // 50 */ // In Redux, state updates must be immutable: /* function todoReducer(state = [], action) { switch (action.type) { case 'ADD_TODO': // Return a new array with the new todo added return [ ...state, { id: action.id, text: action.text, completed: false } ]; case 'TOGGLE_TODO': // Return a new array with the updated todo return state.map(todo => todo.id === action.id ? { ...todo, completed: !todo.completed } : todo ); default: return state; } } */ // React state updates should be immutable: /* function Counter() { const [user, setUser] = useState({ name: 'Alice', age: 30 }); const incrementAge = () => { // Create a new object instead of modifying existing state setUser({ ...user, age: user.age + 1 }); }; return ( <div> <p>{user.name} is {user.age} years old</p> <button onClick={incrementAge}>Increment Age</button> </div> ); } */ ``` ## Web Fundamentals (The Building Blocks) ### DOM (Document Object Model) - Tree Data Structure **CS Concept:** Tree data structure with nodes, edges, and traversal algorithms. **Web Development Example:** ```javascript // The DOM is a tree of nodes representing the HTML document const rootNode = document.documentElement; // <html> element (root of the tree) const bodyNode = document.body; // <body> element (child node) const firstParagraph = document.querySelector('p'); // Find a specific node // Tree traversal - accessing parent, children, and siblings (like tree traversal algorithms) const parent = firstParagraph.parentNode; const children = parent.childNodes; const nextSibling = firstParagraph.nextSibling; // This is conceptually similar to traversing a binary tree in CS /* class TreeNode { TreeNode parent; TreeNode[] children; TreeNode nextSibling; // ... } */ ``` ### HTML - Markup Language/Structured Document Format **CS Concept:** Structured format for data representation. **Web Development Example:** ```html <!-- HTML defines a hierarchical structure similar to a tree data structure --> <html> <head> <title>Page Title</title> <!-- Child node of head --> </head> <body> <h1>Heading</h1> <!-- Child node of body --> <p>Paragraph with <a href="link.html">link</a>.</p> <!-- Nested nodes --> </body> </html> <!-- This is conceptually similar to XML or a tree data structure in CS --> <!-- struct Node { string tagName; Map<string, string> attributes; List<Node> children; } --> ``` ### CSS - Declarative Rule System **CS Concept:** Declarative programming where you specify what should happen, not how. **Web Development Example:** ```css /* CSS uses selectors (similar to querying) and declarations (key-value pairs) */ h1 { color: blue; /* Property: value pairs define appearance */ font-size: 24px; } /* Specificity is an algorithm for determining which rule applies */ .article h1 { /* More specific selector takes precedence */ color: red; /* This will override the blue color for h1 elements inside .article */ } /* This is similar to declarative programming in CS, like SQL or Prolog */ /* SELECT * FROM elements WHERE tagName = 'h1' AND parent.class = 'article' THEN SET color = 'red', font-size = '24px' */ ``` ### JavaScript - General-purpose Programming Language **CS Concept:** Turing-complete language with functions as first-class citizens. **Web Development Example:** ```javascript // JavaScript supports various programming paradigms // Functional programming (functions as first-class citizens) const numbers = [1, 2, 3, 4, 5]; const doubled = numbers.map(x => x * 2); // [2, 4, 6, 8, 10] // Object-oriented programming (prototype-based, not class-based like Java) function Person(name) { this.name = name; } Person.prototype.greet = function() { return `Hello, I'm ${this.name}`; }; const alice = new Person('Alice'); console.log(alice.greet()); // "Hello, I'm Alice" // In CS terms, this is similar to: /* class Person { String name; Person(String name) { this.name = name; } String greet() { return "Hello, I'm " + this.name; } } */ ``` ### HTTP/HTTPS - Application Layer Protocol **CS Concept:** Request-response protocol in client-server architecture. **Web Development Example:** ```javascript // Making an HTTP request (similar to network socket programming) fetch('https://api.example.com/data') .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Error:', error)); // In CS networking terms, this is similar to: /* Socket socket = new Socket("api.example.com", 443); OutputStream out = socket.getOutputStream(); out.write("GET /data HTTP/1.1\r\nHost: api.example.com\r\n\r\n"); InputStream in = socket.getInputStream(); // Read and parse the response */ ``` ## DOM Manipulation and Events ### DOM Manipulation - Tree Modification Algorithms **CS Concept:** Tree insertion, deletion, and modification operations. **Web Development Example:** ```javascript // Creating and adding a new node (similar to inserting a node in a tree) const newParagraph = document.createElement('p'); // Create a new node newParagraph.textContent = 'New paragraph'; // Set node content document.body.appendChild(newParagraph); // Add to the tree (similar to tree.insert()) // Removing a node (similar to removing a node from a tree) const elementToRemove = document.querySelector('.old-content'); if (elementToRemove) { elementToRemove.parentNode.removeChild(elementToRemove); // Similar to tree.remove() } // Modifying a node (updating a node's properties) const heading = document.querySelector('h1'); heading.textContent = 'Updated Heading'; // Similar to node.value = newValue heading.style.color = 'red'; // Modify attributes ``` ### Event Handling - Observer Pattern **CS Concept:** Observer pattern where objects subscribe to events. **Web Development Example:** ```javascript // Adding an event listener (subscribing to an event) const button = document.querySelector('button'); button.addEventListener('click', function(event) { console.log('Button clicked!', event); }); // Event propagation through the tree (event bubbling) document.body.addEventListener('click', function(event) { console.log('Body clicked!', event.target); // event.target is the element that was actually clicked }); // This is similar to the Observer pattern in CS: /* class Button { List<ClickListener> listeners = new ArrayList<>(); void addClickListener(ClickListener listener) { listeners.add(listener); } void click() { for (ClickListener listener : listeners) { listener.onClick(new ClickEvent(this)); } } } */ ``` ## JavaScript Core Concepts ### Closures - Lexical Scoping/Captured Environment **CS Concept:** Functions that remember their lexical environment. **Web Development Example:** ```javascript // Closure: a function that remembers its lexical scope function createCounter() { let count = 0; // Private variable in the closure return function() { return ++count; // Access to the enclosing function's variables }; } const counter = createCounter(); console.log(counter()); // 1 console.log(counter()); // 2 // This is similar to lexical scoping in functional languages: /* def create_counter(): count = 0 def increment(): nonlocal count count += 1 return count return increment */ ``` ### Promises - Future/Deferred Computation **CS Concept:** Object representing a value that may be available in the future. **Web Development Example:** ```javascript // Promise: represents an asynchronous operation function fetchData(url) { return new Promise((resolve, reject) => { // Asynchronous operation fetch(url) .then(response => { if (response.ok) { return response.json(); } throw new Error('Network response was not ok'); }) .then(data => resolve(data)) .catch(error => reject(error)); }); } // Using the promise fetchData('https://api.example.com/data') .then(data => console.log('Data received:', data)) .catch(error => console.error('Error:', error)); // This is similar to Futures/Promises in CS: /* Future<String> future = executorService.submit(() -> { // Perform async operation return result; }); future.thenApply(result -> { // Process result return processedResult; }); */ ``` ### Async/Await - Syntactic Sugar for Promises/Coroutines **CS Concept:** Sequential-looking code for asynchronous operations. **Web Development Example:** ```javascript // Async/await: syntactic sugar for promises async function fetchUserData(userId) { try { // This looks synchronous but is actually asynchronous const response = await fetch(`https://api.example.com/users/${userId}`); if (!response.ok) { throw new Error('User not found'); } const userData = await response.json(); return userData; } catch (error) { console.error('Error fetching user:', error); throw error; } } // Using the async function async function displayUser(userId) { try { const user = await fetchUserData(userId); console.log('User data:', user); } catch (error) { console.error('Failed to display user:', error); } } // This is similar to coroutines or cooperative multitasking in CS: /* suspend fun fetchUserData(userId: String): User { val response = api.getUser(userId) return response.user } */ ``` ## Web Components & Frameworks ### Components - Encapsulated UI Elements **CS Concept:** Modular, reusable classes/functions with encapsulated state. **Web Development Example (React):** ```javascript // React component (a function that returns UI elements) function UserProfile({ userId, name, email }) { // State (similar to instance variables in a class) const [isExpanded, setIsExpanded] = React.useState(false); // Event handler (method) const toggleExpand = () => { setIsExpanded(prevState => !prevState); }; // Render method (returns UI) return ( <div className="user-profile"> <h2>{name}</h2> <button onClick={toggleExpand}> {isExpanded ? 'Show Less' : 'Show More'} </button> {isExpanded && ( <div className="details"> <p>Email: {email}</p> <p>ID: {userId}</p> </div> )} </div> ); } // This is similar to a class with encapsulated state and methods in OOP: /* class UserProfile { private String userId; private String name; private String email; private boolean isExpanded = false; public UserProfile(String userId, String name, String email) { this.userId = userId; this.name = name; this.email = email; } public void toggleExpand() { this.isExpanded = !this.isExpanded; this.render(); } public Element render() { // Return UI representation } } */ ``` ### Virtual DOM - Tree Diffing Algorithm **CS Concept:** Optimized tree comparison and manipulation. **Web Development Example (Simplified React-like Implementation):** ```javascript // Simplified Virtual DOM implementation (conceptual) function createVirtualElement(type, props, ...children) { return { type, props, children }; } // Creating a virtual DOM tree const virtualTree = createVirtualElement('div', { className: 'container' }, createVirtualElement('h1', {}, 'Title'), createVirtualElement('p', {}, 'Content') ); // Diffing algorithm (simplified) function diff(oldTree, newTree) { // If node types are different, replace the entire node if (oldTree.type !== newTree.type) { return { action: 'REPLACE', newNode: newTree }; } // If props changed, update props if (JSON.stringify(oldTree.props) !== JSON.stringify(newTree.props)) { return { action: 'UPDATE_PROPS', newProps: newTree.props }; } // Recursively diff children const childPatches = []; for (let i = 0; i < Math.max(oldTree.children.length, newTree.children.length); i++) { if (i >= oldTree.children.length) { childPatches.push({ action: 'APPEND', newNode: newTree.children[i] }); } else if (i >= newTree.children.length) { childPatches.push({ action: 'REMOVE' }); } else { childPatches.push(diff(oldTree.children[i], newTree.children[i])); } } return { action: 'UPDATE_CHILDREN', childPatches }; } // This is similar to tree diffing algorithms in CS: /* function diffTrees(Tree oldTree, Tree newTree) { List<Patch> patches = new ArrayList<>(); if (oldTree.type != newTree.type) { patches.add(new ReplacePatch(newTree)); } else { // Compare properties and children } return patches; } */ ``` ## HTTP and Data Fetching ### REST API - Resource-Oriented Architecture **CS Concept:** Client-server architecture with standardized operations on resources. **Web Development Example:** ```javascript // RESTful API client const apiClient = { baseUrl: 'https://api.example.com', // GET request (read) async getUser(userId) { const response = await fetch(`${this.baseUrl}/users/${userId}`); if (!response.ok) throw new Error('Failed to fetch user'); return response.json(); }, // POST request (create) async createUser(userData) { const response = await fetch(`${this.baseUrl}/users`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(userData) }); if (!response.ok) throw new Error('Failed to create user'); return response.json(); }, // PUT request (update) async updateUser(userId, userData) { const response = await fetch(`${this.baseUrl}/users/${userId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(userData) }); if (!response.ok) throw new Error('Failed to update user'); return response.json(); }, // DELETE request (delete) async deleteUser(userId) { const response = await fetch(`${this.baseUrl}/users/${userId}`, { method: 'DELETE' }); if (!response.ok) throw new Error('Failed to delete user'); return true; } }; // This is similar to RPC (Remote Procedure Call) in distributed systems: /* interface UserService { User getUser(String userId); User createUser(UserData userData); User updateUser(String userId, UserData userData); boolean deleteUser(String userId); } */ ``` ### GraphQL - Type System + Query Language **CS Concept:** Strongly-typed query language for APIs. **Web Development Example:** ```javascript // GraphQL query const query = ` query GetUserWithPosts($userId: ID!) { user(id: $userId) { id name email posts { id title content createdAt } } } `; // Executing the query async function fetchUserWithPosts(userId) { const response = await fetch('https://api.example.com/graphql', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query, variables: { userId } }) }); const { data, errors } = await response.json(); if (errors) { throw new Error(errors.map(e => e.message).join(', ')); } return data.user; } // This is similar to strongly-typed query languages in databases: /* SELECT u.id, u.name, u.email, p.id, p.title, p.content, p.created_at FROM users u LEFT JOIN posts p ON u.id = p.user_id WHERE u.id = ? */ ``` ## State Management ### Redux - State Machine with Immutable Updates **CS Concept:** Predictable state container based on pure functions. **Web Development Example:** ```javascript // Action types (events in state machine) const ADD_TODO = 'ADD_TODO'; const TOGGLE_TODO = 'TOGGLE_TODO'; // Action creators (event generators) function addTodo(text) { return { type: ADD_TODO, payload: { text } }; } function toggleTodo(id) { return { type: TOGGLE_TODO, payload: { id } }; } // Reducer (pure function for state transitions) function todosReducer(state = [], action) { switch (action.type) { case ADD_TODO: // Create new state rather than modifying existing (immutability) return [ ...state, { id: state.length + 1, text: action.payload.text, completed: false } ]; case TOGGLE_TODO: // Map creates a new array (immutability) return state.map(todo => { if (todo.id === action.payload.id) { // Create new object with updated property (immutability) return { ...todo, completed: !todo.completed }; } return todo; }); default: return state; } } // This is similar to state machines and pure functions in functional programming: /* enum Event { ADD_TODO, TOGGLE_TODO } class State { List<Todo> todos; } State reducer(State state, Event event) { switch (event) { case Event.ADD_TODO: // Create new state without modifying original return new State(state.todos.append(new Todo(...))); case Event.TOGGLE_TODO: // Create new state without modifying original return new State(state.todos.map(...)); } } */ ``` ### React Hooks - Function-based State and Effects **CS Concept:** Functions with side effects and memoization. **Web Development Example:** ```javascript // Custom hook for a counter function useCounter(initialValue = 0, step = 1) { // State hook (adds state to a function component) const [count, setCount] = React.useState(initialValue); // Function to increment counter const increment = () => { setCount(prevCount => prevCount + step); }; // Function to decrement counter const decrement = () => { setCount(prevCount => prevCount - step); }; // Effect hook (handles side effects) React.useEffect(() => { // Side effect: update document title when count changes document.title = `Count: ${count}`; // Cleanup function (similar to destructor) return () => { document.title = 'React App'; }; }, [count]); // Dependency array (like memoization) // Return state and functions return { count, increment, decrement }; } // Using the custom hook in a component function CounterDisplay() { const { count, increment, decrement } = useCounter(); return ( <div> <p>Count: {count}</p> <button onClick={increment}>Increment</button> <button onClick={decrement}>Decrement</button> </div> ); } // This is similar to functional programming with side effects: /* class Counter { private int count; public Counter(int initialValue) { this.count = initialValue; updateTitle(); } public void increment() { this.count++; updateTitle(); } public void decrement() { this.count--; updateTitle(); } private void updateTitle() { // Side effect document.title = "Count: " + this.count; } public void cleanup() { document.title = "React App"; } } */ ``` ## Web Security ### Cross-Site Scripting (XSS) - Code Injection Attack **CS Concept:** Injection vulnerabilities and input sanitization. **Web Development Example:** ```javascript // Vulnerable code (allows XSS) function displayUserInput(input) { // DON'T DO THIS - Directly inserting user input into HTML document.getElementById('output').innerHTML = input; } // Safe code (prevents XSS) function displayUserInputSafely(input) { // DO THIS - Escape HTML special characters const escaped = input .replace(/&/g, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/"/g, '&quot;') .replace(/'/g, '&#039;'); document.getElementById('output').innerHTML = escaped; // Even better, use textContent which automatically escapes HTML document.getElementById('output-safe').textContent = input; } // This is similar to input validation and sanitization in CS: /* String sanitizeInput(String input) { // Replace special characters with escaped versions return input .replace("&", "&amp;") .replace("<", "&lt;") .replace(">", "&gt;"); } */ ``` ### Content Security Policy (CSP) - Security Policy Language **CS Concept:** Access control and security policies. **Web Development Example:** ```html <!-- HTML header with CSP --> <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://trusted-cdn.com; style-src 'self' https://trusted-styles.com; img-src 'self' https://trusted-images.com"> <!-- This tells the browser to: 1. Only load resources from the same origin by default 2. Only execute scripts from same origin or trusted-cdn.com 3. Only load styles from same origin or trusted-styles.com 4. Only load images from same origin or trusted-images.com --> <!-- Server-side implementation (Node.js with Express) --> <script> const express = require('express'); const helmet = require('helmet'); // Security middleware const app = express(); // Set CSP headers app.use(helmet.contentSecurityPolicy({ directives: { defaultSrc: ["'self'"], scriptSrc: ["'self'", "https://trusted-cdn.com"], styleSrc: ["'self'", "https://trusted-styles.com"], imgSrc: ["'self'", "https://trusted-images.com"] } })); // This is similar to access control policies in security: /* SecurityPolicy policy = new SecurityPolicy(); policy.addRule("script", "same-origin"); policy.addRule("script", "https://trusted-cdn.com"); policy.enforce(); */ </script> ``` ## Performance Optimization ### Code Splitting - Dynamic Loading **CS Concept:** Dynamic linking and lazy loading. **Web Development Example (using Webpack and React):** ```javascript // Without code splitting (everything in one bundle) import { Home } from './Home'; import { About } from './About'; import { Contact } from './Contact'; function App() { // ... } // With code splitting (components loaded on demand) import React, { Suspense, lazy } from 'react'; // Lazy load components (similar to dynamic linking) const Home = lazy(() => import('./Home')); const About = lazy(() => import('./About')); const Contact = lazy(() => import('./Contact')); function App() { return ( <div> <Suspense fallback={<div>Loading...</div>}> {/* Components are loaded only when needed */} <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> <Route path="/contact" element={<Contact />} /> </Routes> </Suspense> </div> ); } // This is similar to dynamic loading in CS: /* class DynamicLoader { Module loadModule(String moduleName) { // Load the module only when requested File moduleFile = new File(moduleName + ".so"); return System.loadLibrary(moduleFile); } } */ ``` ### Memoization - Caching Function Results **CS Concept:** Dynamic programming and cache optimization. **Web Development Example:** ```javascript // Pure function without memoization function fibonacci(n) { if (n <= 1) return n; return fibonacci(n - 1) + fibonacci(n - 2); } // fibonacci(40) is very slow due to repeated calculations // With memoization (caching results) function memoizedFibonacci() { const cache = {}; // Cache to store results return function fib(n) { // If result is in cache, return it if (n in cache) { return cache[n]; } // Calculate result for first time let result; if (n <= 1) { result = n; } else { result = fib(n - 1) + fib(n - 2); } // Cache the result cache[n] = result; return result; }; } const fastFibonacci = memoizedFibonacci(); // fastFibonacci(40) is much faster // React's useMemo hook (component-level memoization) function ExpensiveComponent({ data }) { // Only recalculate when data changes const processedData = React.useMemo(() => { // Expensive calculation return data.map(item => expensiveTransformation(item)); }, [data]); // Dependency array return <div>{processedData.map(item => <div key={item.id}>{item.value}</div>)}</div>; } // This is similar to dynamic programming in CS: /* class FibonacciCalculator { private Map<Integer, Integer> cache = new HashMap<>(); public int fibonacci(int n) { // Check if result is in cache if (cache.containsKey(n)) { return cache.get(n); } // Calculate and cache result int result; if (n <= 1) { result = n; } else { result = fibonacci(n - 1) + fibonacci(n - 2); } cache.put(n, result); return result; } } */ ``` ## Modern Web Capabilities ### Service Workers - Background Processing **CS Concept:** Background processes and proxy servers. **Web Development Example:** ```javascript // Registering a service worker if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/service-worker.js') .then(registration => { console.log('Service Worker registered with scope:', registration.scope); }) .catch(error => { console.error('Service Worker registration failed:', error); }); } // service-worker.js const CACHE_NAME = 'my-site-cache-v1'; const urlsToCache = [ '/', '/styles/main.css', '/scripts/main.js', '/images/logo.png' ]; // Install event - cache assets (similar to initialization/setup) self.addEventListener('install', event => { event.waitUntil( caches.open(CACHE_NAME) .then(cache => { console.log('Opened cache'); return cache.addAll(urlsToCache); }) ); }); // Fetch event - intercept network requests (similar to proxy server) self.addEventListener('fetch', event => { event.respondWith( caches.match(event.request) .then(response => { // Cache hit - return the response from cache if (response) { return response; } // Clone the request (streams can only be consumed once) const fetchRequest = event.request.clone(); // Try the network return fetch(fetchRequest).then(response => { // Check if valid response if (!response || response.status !== 200 || response.type !== 'basic') { return response; } // Clone the response (streams can only be consumed once) const responseToCache = response.clone(); // Add response to cache for future caches.open(CACHE_NAME) .then(cache => { cache.put(event.request, responseToCache); }); return response; }); }) ); }); // This is similar to proxy servers and caching in CS: /* class CachingProxy { private Map<String, Response> cache = new HashMap<>(); public Response fetch(Request request) { // Check cache first if (cache.containsKey(request.url)) { return cache.get(request.url); } // Fetch from network Response response = network.fetch(request); // Cache the response if (response.isValid()) { cache.put(request.url, response); } return response; } } */ ``` ### WebAssembly (WASM) - Low-Level Bytecode Format **CS Concept:** Bytecode and virtual machines. **Web Development Example:** ```javascript // Loading and using a WebAssembly module async function loadWasmModule() { try { // Fetch the WebAssembly module const response = await fetch('/fibonacci.wasm'); const buffer = await response.arrayBuffer(); // Compile and instantiate the module const wasmModule = await WebAssembly.instantiate(buffer, { env: { // JavaScript functions accessible to WASM printResult: (result) => { console.log('Fibonacci result:', result); } } }); // Extract the instance with exported functions const instance = wasmModule.instance; // Call the exported function const result = instance.exports.fibonacci(40); console.log(`Fibonacci(40) = ${result}`); return instance; } catch (error) { console.error('Error loading WASM module:', error); } } // This is similar to bytecode and VMs in CS: /* class BytecodeVM { private byte[] bytecode; private Map<String, Function> imports; public BytecodeVM(byte[] bytecode, Map<String, Function> imports) { this.bytecode = bytecode; this.imports = imports; } public Object execute(String functionName, Object... args) { // Load function from bytecode Function function = findFunction(functionName); // Execute bytecode instructions return function.call(args); } } */ ``` ## Testing ### Unit Testing - Function/Module Verification **CS Concept:** Test-driven development and verification. **Web Development Example (using Jest):** ```javascript // Function to test function sum(a, b) { return a + b; } // User authentication function function authenticate(username, password) { if (!username || !password) { return { success: false, message: 'Missing credentials' }; } // In a real app, this would check against a database if (username === 'admin' && password === 'secret123') { return { success: true, user: { id: 1, username: 'admin', role: 'admin' } }; } return { success: false, message: 'Invalid credentials' }; } // Unit tests describe('Math functions', () => { test('sum adds two numbers correctly', () => { expect(sum(2, 3)).toBe(5); expect(sum(-1, 1)).toBe(0); expect(sum(0, 0)).toBe(0); }); }); describe('Authentication', () => { test('returns success with valid credentials', () => { const result = authenticate('admin', 'secret123'); expect(result.success).toBe(true); expect(result.user).toBeDefined(); expect(result.user.role).toBe('admin'); }); test('returns failure with invalid credentials', () => { const result = authenticate('admin', 'wrongpassword'); expect(result.success).toBe(false); expect(result.message).toBeDefined(); }); test('returns failure with missing credentials', () => { expect(authenticate('', 'secret123').success).toBe(false); expect(authenticate('admin', '').success).toBe(false); expect(authenticate('', '').success).toBe(false); }); }); // This is similar to unit testing in CS: /* public class MathTest { @Test public void testSum() { assertEquals(5, sum(2, 3)); assertEquals(0, sum(-1, 1)); assertEquals(0, sum(0, 0)); } } public class AuthenticationTest { @Test public void testValidCredentials() { AuthResult result = authenticate("admin", "secret123"); assertTrue(result.isSuccess()); assertNotNull(result.getUser()); assertEquals("admin", result.getUser().getRole()); } // Additional tests... } */ ``` ## Backend and Full-Stack Concepts ### Server-Side Rendering (SSR) - Server-Generated UI **CS Concept:** Template processing and view generation. **Web Development Example (using Node.js and Express):** ```javascript // Server-side rendering with Express and EJS template engine const express = require('express'); const app = express(); // Set EJS as the template engine app.set('view engine', 'ejs'); // Data (in a real app, this would come from a database) const products = [ { id: 1, name: 'Laptop', price: 999.99 }, { id: 2, name: 'Smartphone', price: 699.99 }, { id: 3, name: 'Tablet', price: 349.99 } ]; // Route that renders a template app.get('/products', (req, res) => { // Render the 'products' template with data res.render('products', { title: 'Our Products', products: products, userLoggedIn: req.session?.user != null }); }); // Template file (products.ejs) /* <!DOCTYPE html> <html> <head> <title><%= title %></title> </head> <body> <h1><%= title %></h1> <% if (userLoggedIn) { %> <p>Welcome back! Here are our latest products:</p> <% } else { %> <p>Log in to see special offers!</p> <% } %> <ul> <% products.forEach(function(product) { %> <li> <%= product.name %> - lt;%= product.price.toFixed(2) %> </li> <% }); %> </ul> </body> </html> */ // This is similar to view rendering in MVC frameworks: /* public class ProductController { private ProductRepository repository; public ModelAndView showProducts(HttpSession session) { List<Product> products = repository.findAll(); ModelAndView modelAndView = new ModelAndView("products"); modelAndView.addAttribute("title", "Our Products"); modelAndView.addAttribute("products", products); modelAndView.addAttribute("userLoggedIn", session.getAttribute("user") != null); return modelAndView; } } */ ``` ### ORM (Object-Relational Mapping) - Data Abstraction Layer **CS Concept:** Data abstraction and persistence mapping. **Web Development Example (using Sequelize with Node.js):** ```javascript // Defining a model (similar to a class definition) const { Sequelize, DataTypes } = require('sequelize'); const sequelize = new Sequelize('database', 'username', 'password', { host: 'localhost', dialect: 'mysql' }); // User model definition const User = sequelize.define('User', { // Model attributes (similar to class properties) id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true }, username: { type: DataTypes.STRING, allowNull: false, unique: true }, email: { type: DataTypes.STRING, allowNull: false, validate: { isEmail: true } }, role: { type: DataTypes.ENUM('user', 'admin'), defaultValue: 'user' }, lastLogin: { type: DataTypes.DATE } }); // Post model definition const Post = sequelize.define('Post', { title: { type: DataTypes.STRING, allowNull: false }, content: { type: DataTypes.TEXT }, published: { type: DataTypes.BOOLEAN, defaultValue: false } }); // Define relationships (similar to database foreign keys) User.hasMany(Post); Post.belongsTo(User); // Using the models (CRUD operations) async function createUser() { try { // Create a new user (similar to instantiating a class) const user = await User.create({ username: 'johndoe', email: '[email protected]', role: 'user' }); console.log('User created:', user.toJSON()); return user; } catch (error) { console.error('Error creating user:', error); } } async function findUsers() { try { // Query users (similar to database queries) const users = await User.findAll({ where: { role: 'admin' }, include: [Post] // Include related posts (similar to JOIN) }); console.log(`Found ${users.length} admin users`); return users; } catch (error) { console.error('Error finding users:', error); } } // This is similar to ORM concepts in CS: /* @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(unique = true, nullable = false) private String username; @Column(nullable = false) private String email; @Enumerated(EnumType.STRING) private Role role = Role.USER; @Column(name = "last_login") private Date lastLogin; @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) private List<Post> posts; // Getters and setters... } */ ``` ### Microservices - Distributed Systems Architecture **CS Concept:** Service-oriented architecture and distributed systems. **Web Development Example:** ```javascript // User Service (auth-service.js) const express = require('express'); const app = express(); app.use(express.json()); // In-memory user database (in real app, this would be a proper database) const users = [ { id: 1, username: 'alice', passwordHash: 'hashed_password_1', role: 'user' }, { id: 2, username: 'bob', passwordHash: 'hashed_password_2', role: 'admin' } ]; // Authentication endpoint app.post('/auth/login', (req, res) => { const { username, password } = req.body; // Find user (simplified authentication) const user = users.find(u => u.username === username); if (!user || !validatePassword(password, user.passwordHash)) { return res.status(401).json({ error: 'Invalid credentials' }); } // Generate JWT token (simplified) const token = generateToken(user); res.json({ token, userId: user.id, role: user.role }); }); app.listen(3001, () => { console.log('Auth service running on port 3001'); }); // Product Service (product-service.js) const express = require('express'); const app = express(); app.use(express.json()); // In-memory product database const products = [ { id: 1, name: 'Laptop', price: 999.99, stock: 50 }, { id: 2, name: 'Smartphone', price: 699.99, stock: 100 }, { id: 3, name: 'Tablet', price: 349.99, stock: 75 } ]; // Authentication middleware (verifies token from auth service) function authenticate(req, res, next) { const token = req.headers.authorization?.split(' ')[1]; if (!token) { return res.status(401).json({ error: 'Authentication required' }); } try { // Verify token (simplified) const decoded = verifyToken(token); req.user = decoded; next(); } catch (error) { res.status(401).json({ error: 'Invalid token' }); } } // Get all products app.get('/products', (req, res) => { res.json(products); }); // Get product by ID app.get('/products/:id', (req, res) => { const product = products.find(p => p.id === parseInt(req.params.id)); if (!product) { return res.status(404).json({ error: 'Product not found' }); } res.json(product); }); // Create product (requires authentication) app.post('/products', authenticate, (req, res) => { // Check if user is admin if (req.user.role !== 'admin') { return res.status(403).json({ error: 'Permission denied' }); } const { name, price, stock } = req.body; const newProduct = { id: products.length + 1, name, price, stock }; products.push(newProduct); res.status(201).json(newProduct); }); app.listen(3002, () => { console.log('Product service running on port 3002'); }); // API Gateway (gateway.js) const express = require('express'); const { createProxyMiddleware } = require('http-proxy-middleware'); const app = express(); // Route to auth service app.use('/auth', createProxyMiddleware({ target: 'http://localhost:3001', pathRewrite: {'^/auth': '/auth'} })); // Route to product service app.use('/products', createProxyMiddleware({ target: 'http://localhost:3002', pathRewrite: {'^/products': '/products'} })); app.listen(3000, () => { console.log('API Gateway running on port 3000'); }); // This is similar to distributed systems in CS: /* interface AuthService { AuthResponse authenticate(String username, String password); } interface ProductService { List<Product> getAllProducts(); Product getProductById(Long id); Product createProduct(Product product); } class ApiGateway { private final AuthService authService; private final ProductService productService; public Response handleRequest(Request request) { if (request.getPath().startsWith("/auth")) { return routeToAuthService(request); } else if (request.getPath().startsWith("/products")) { return routeToProductService(request); } return new Response(404, "Not Found"); } } */ ``` ## CSS Concepts ### CSS Box Model - Layout Algorithm **CS Concept:** Layout calculation and rendering model. **Web Development Example:** ```css /* CSS Box Model demonstration */ .box { /* Content dimensions */ width: 200px; height: 100px; /* Padding (inside the border) */ padding-top: 10px; padding-right: 20px; padding-bottom: 10px; padding-left: 20px; /* Border */ border-width: 5px; border-style: solid; border-color: #333; /* Margin (outside the border) */ margin: 15px; /* Background (fills content and padding areas) */ background-color: #f0f0f0; } /* Total width calculation: 200px (content) + 20px (left padding) + 20px (right padding) + 5px (left border) + 5px (right border) = 250px Total height calculation: 100px (content) + 10px (top padding) + 10px (bottom padding) + 5px (top border) + 5px (bottom border) = 130px Space occupied including margin: 250px (total width) + 15px (left margin) + 15px (right margin) = 280px 130px (total height) + 15px (top margin) + 15px (bottom margin) = 160px */ /* Alternative box sizing (more intuitive) */ .border-box { /* Make width/height include padding and border */ box-sizing: border-box; width: 200px; /* Total width including padding and border will be 200px */ height: 100px; /* Total height including padding and border will be 100px */ padding: 10px; border: 5px solid #333; margin: 15px; } /* This is similar to layout algorithms in graphics programming: class BoxModel { int contentWidth; int contentHeight; Insets padding; Insets border; Insets margin; // Calculate total width int getTotalWidth() { return contentWidth + padding.left + padding.right + border.left + border.right; } // Calculate total height int getTotalHeight() { return contentHeight + padding.top + padding.bottom + border.top + border.bottom; } } */ ``` ### Flexbox - Layout Algorithm **CS Concept:** Constraint-based layout system. **Web Development Example:** ```css /* Flexbox Container */ .container { display: flex; /* Main axis direction */ flex-direction: row; /* horizontal (default) */ /* flex-direction: column; */ /* vertical */ /* How items wrap when they don't fit */ flex-wrap: wrap; /* Alignment along main axis */ justify-content: space-between; /* Options: flex-start, flex-end, center, space-around, space-evenly */ /* Alignment along cross axis */ align-items: center; /* Options: flex-start, flex-end, stretch, baseline */ /* Gap between items */ gap: 10px; /* Container styling */ border: 2px solid #333; padding: 10px; } /* Flex Items */ .item { background-color: #f0f0f0; padding: 20px; border: 1px solid #999; text-align: center; /* Flex grow (proportion of extra space to take) */ flex-grow: 1; /* Flex shrink (how much item can shrink) */ flex-shrink: 1; /* Flex basis (initial main size) */ flex-basis: 100px; /* Shorthand for grow, shrink, basis */ /* flex: 1 1 100px; */ } /* Make the second item grow twice as much */ .item:nth-child(2) { flex-grow: 2; background-color: #e0e0ff; } /* Make the third item not grow */ .item:nth-child(3) { flex-grow: 0; flex-shrink: 0; /* Also prevents shrinking */ background-color: #ffe0e0; } /* This is similar to constraint-based layout in CS: class FlexLayout { enum Direction { ROW, COLUMN } enum Alignment { START, END, CENTER, SPACE_BETWEEN, SPACE_AROUND } Direction direction; Alignment mainAxisAlignment; Alignment crossAxisAlignment; boolean wrap; List<FlexItem> items; void calculateLayout(int availableWidth, int availableHeight) { // Distribute space among items based on flex properties int remainingSpace = (direction == Direction.ROW) ? availableWidth : availableHeight; // Subtract fixed-size items for (FlexItem item : items) { if (item.flexGrow == 0) { remainingSpace -= item.size; } } // Calculate total flex units int totalFlexUnits = 0; for (FlexItem item : items) { totalFlexUnits += item.flexGrow; } // Distribute remaining space for (FlexItem item : items) { if (item.flexGrow > 0) { item.allocatedSize = item.baseSize + (remainingSpace * item.flexGrow / totalFlexUnits); } } } } */ ``` ## Advanced JavaScript ### Promises and Async Programming - Concurrency Model **CS Concept:** Asynchronous programming and concurrency. **Web Development Example:** ```javascript // Promise-based asynchronous function function fetchUserData(userId) { return new Promise((resolve, reject) => { // Simulate API call with setTimeout setTimeout(() => { if (userId > 0) { const userData = { id: userId, name: `User ${userId}`, email: `user${userId}@example.com` }; resolve(userData); // Success case } else { reject(new Error('Invalid user ID')); // Error case } }, 1000); // 1 second delay }); } // Using promises with then/catch function displayUserWithPromises(userId) { console.log(`Fetching data for user ${userId}...`); fetchUserData(userId) .then(user => { console.log('User data received:', user); return fetchUserPosts(user.id); // Return another promise }) .then(posts => { console.log('User posts received:', posts); }) .catch(error => { console.error('Error:', error.message); }) .finally(() => { console.log('Operation completed'); }); console.log('Request initiated'); // This runs before the promises resolve } // Using async/await (cleaner syntax for promises) async function displayUserWithAsync(userId) { console.log(`Fetching data for user ${userId}...`); try { console.log('Request initiated'); // await pauses execution until promise resolves const user = await fetchUserData(userId); console.log('User data received:', user); const posts = await fetchUserPosts(user.id); console.log('User posts received:', posts); } catch (error) { console.error('Error:', error.message); } finally { console.log('Operation completed'); } } // Promise combinators async function fetchMultipleUsers(userIds) { try { // Promise.all - wait for all promises to resolve (parallel execution) const users = await Promise.all( userIds.map(id => fetchUserData(id)) ); console.log('All users:', users); // Promise.race - resolve/reject as soon as the first promise resolves/rejects const fastestResponse = await Promise.race([ fetchUserData(1).then(() => 'User API won'), fetchWeatherData().then(() => 'Weather API won') ]); console.log('Fastest response:', fastestResponse); // Promise.allSettled - wait for all promises to settle (resolve or reject) const results = await Promise.allSettled( userIds.map(id => fetchUserData(id)) ); // Handle mixed results results.forEach((result, index) => { if (result.status === 'fulfilled') { console.log(`User ${userIds[index]} data:`, result.value); } else { console.log(`User ${userIds[index]} error:`, result.reason.message); } }); } catch (error) { console.error('Error:', error.message); } } // This is similar to concurrent programming in CS: /* class AsyncOperation<T> { private CompletableFuture<T> future; public AsyncOperation<T> then(Function<T, U> callback) { return new AsyncOperation<>(future.thenApply(callback)); } public AsyncOperation<T> catch(Function<Exception, T> handler) { return new AsyncOperation<>(future.exceptionally(handler)); } public static <T> AsyncOperation<List<T>> all(List<AsyncOperation<T>> operations) { List<CompletableFuture<T>> futures = operations.stream() .map(op -> op.future) .collect(Collectors.toList()); CompletableFuture<List<T>> allFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) .thenApply(v -> futures.stream() .map(CompletableFuture::join) .collect(Collectors.toList())); return new AsyncOperation<>(allFuture); } } */ ``` ### Progressive Web Apps (PWAs) - Hybrid Application Model **CS Concept:** Offline-capable web applications with native features. #chromeExtension **Web Development Example:** ```javascript // manifest.json - Application metadata (similar to app descriptor) /* { "name": "Weather App", "short_name": "Weather", "description": "Check weather forecasts", "start_url": "/", "display": "standalone", "background_color": "#ffffff", "theme_color": "#4285f4", "icons": [ { "src": "/icons/icon-72x72.png", "sizes": "72x72", "type": "image/png" }, { "src": "/icons/icon-192x192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/icons/icon-512x512.png", "sizes": "512x512", "type": "image/png" } ] } */ // service-worker.js - Offline capabilities const CACHE_NAME = 'weather-app-v1'; const URLS_TO_CACHE = [ '/', '/index.html', '/styles/main.css', '/scripts/app.js', '/images/logo.png', '/offline.html' ]; // Installation event - cache assets self.addEventListener('install', event => { event.waitUntil( caches.open(CACHE_NAME) .then(cache => cache.addAll(URLS_TO_CACHE)) ); }); // Fetch event - serve from cache or network self.addEventListener('fetch', event => { event.respondWith( caches.match(event.request) .then(response => { // Cache hit - return response if (response) { return response; } // Clone the request const fetchRequest = event.request.clone(); return fetch(fetchRequest) .then(response => { // Check if valid response if (!response || response.status !== 200 || response.type !== 'basic') { return response; } // Clone the response const responseToCache = response.clone(); caches.open(CACHE_NAME) .then(cache => { cache.put(event.request, responseToCache); }); return response; }) .catch(() => { // If network fails, serve offline page for navigate requests if (event.request.mode === 'navigate') { return caches.match('/offline.html'); } }); }) ); }); // App.js - Using service worker and other PWA features if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('/service-worker.js') .then(registration => { console.log('ServiceWorker registered with scope:', registration.scope); }) .catch(error => { console.error('ServiceWorker registration failed:', error); }); }); } // Check if app is installed or can be installed let deferredPrompt; window.addEventListener('beforeinstallprompt', event => { // Prevent Chrome from automatically showing the prompt event.preventDefault(); // Stash the event so it can be triggered later deferredPrompt = event; // Show the install button document.getElementById('install-button').style.display = 'block'; }); // Handle install button click document.getElementById('install-button').addEventListener('click', () => { // Hide the install button document.getElementById('install-button').style.display = 'none'; // Show the prompt deferredPrompt.prompt(); // Wait for the user to respond to the prompt deferredPrompt.userChoice.then(choiceResult => { if (choiceResult.outcome === 'accepted') { console.log('User accepted the A2HS prompt'); } else { console.log('User dismissed the A2HS prompt'); } // Clear the saved prompt deferredPrompt = null; }); }); // Using the Cache API directly (for dynamic caching) function cacheApiResponse(url, response) { return caches.open('api-cache') .then(cache => { // Store response in cache with timestamp const clonedResponse = response.clone(); return cache.put(url, clonedResponse); }); } // This is similar to hybrid application development in CS: /* class HybridApp { private CacheManager cacheManager; private NetworkManager networkManager; private LocalStorage localStorage; public Response handleRequest(Request request) { // Try cache first Response cachedResponse = cacheManager.get(request.getUrl()); if (cachedResponse != null) { return cachedResponse; } // Try network try { Response networkResponse = networkManager.fetch(request); cacheManager.put(request.getUrl(), networkResponse.clone()); return networkResponse; } catch (NetworkException e) { // Fall back to offline content return cacheManager.getOfflineResponse(); } } } */ ``` ### OAuth 2.0 - Delegated Authorization Protocol **CS Concept:** Authentication protocols, delegation, and token-based security. **Web Development Example:** ```javascript // OAuth 2.0 client implementation (simplified) class OAuth2Client { constructor(config) { this.clientId = config.clientId; this.clientSecret = config.clientSecret; this.redirectUri = config.redirectUri; this.authUrl = config.authUrl; this.tokenUrl = config.tokenUrl; this.scope = config.scope || ''; } // Generate authorization URL (redirect user to this URL) getAuthorizationUrl() { const params = new URLSearchParams({ client_id: this.clientId, redirect_uri: this.redirectUri, response_type: 'code', scope: this.scope, state: this.generateRandomState() // Prevent CSRF attacks }); return `${this.authUrl}?${params.toString()}`; } // Exchange authorization code for access token async getAccessToken(authorizationCode) { const params = new URLSearchParams({ client_id: this.clientId, client_secret: this.clientSecret, redirect_uri: this.redirectUri, grant_type: 'authorization_code', code: authorizationCode }); const response = await fetch(this.tokenUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: params.toString() }); if (!response.ok) { throw new Error('Failed to get access token'); } return response.json(); // Returns { access_token, refresh_token, expires_in, ... } } // Refresh an expired access token async refreshToken(refreshToken) { const params = new URLSearchParams({ client_id: this.clientId, client_secret: this.clientSecret, grant_type: 'refresh_token', refresh_token: refreshToken }); const response = await fetch(this.tokenUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: params.toString() }); if (!response.ok) { throw new Error('Failed to refresh token'); } return response.json(); // Returns new { access_token, refresh_token, expires_in, ... } } // Generate random state for CSRF protection generateRandomState() { return Math.random().toString(36).substring(2, 15); } } // Usage example - OAuth 2.0 flow in a web application // 1. Initialize the client const oauthClient = new OAuth2Client({ clientId: 'your-client-id', clientSecret: 'your-client-secret', redirectUri: 'https://your-app.com/callback', authUrl: 'https://auth-provider.com/authorize', tokenUrl: 'https://auth-provider.com/token', scope: 'profile email' }); // 2. Redirect user to authorization URL function redirectToLogin() { const authUrl = oauthClient.getAuthorizationUrl(); window.location.href = authUrl; } // 3. Handle callback (in your redirect_uri endpoint) async function handleOAuthCallback() { // Extract code from URL query parameters const urlParams = new URLSearchParams(window.location.search); const code = urlParams.get('code'); const state = urlParams.get('state'); // Verify state (CSRF protection) if (state !== sessionStorage.getItem('oauth_state')) { throw new Error('Invalid state parameter'); } // Exchange code for tokens try { const tokenData = await oauthClient.getAccessToken(code); // Store tokens securely securelyStoreTokens(tokenData); // Redirect to protected area window.location.href = '/dashboard'; } catch (error) { console.error('Authentication error:', error); window.location.href = '/login?error=authentication_failed'; } } // This is similar to authentication protocols in CS: /* public class AuthProtocol { private final String clientId; private final String clientSecret; private final String redirectUri; public String generateAuthorizationRequest() { String state = generateRandomState(); saveStateForVerification(state); return buildAuthorizationUrl(clientId, redirectUri, state); } public TokenResponse exchangeAuthorizationCode(String code) { // Validate and exchange code for token return httpClient.post(tokenUrl) .withFormData("grant_type", "authorization_code") .withFormData("client_id", clientId) .withFormData("client_secret", clientSecret) .withFormData("code", code) .withFormData("redirect_uri", redirectUri) .execute() .parseAs(TokenResponse.class); } } */ ``` ## Data Structures in Web Development ### Trie for Autocomplete - Prefix Tree Implementation **CS Concept:** Trie data structure for efficient prefix searching. **Web Development Example:** ```javascript // Trie data structure for autocomplete functionality class TrieNode { constructor() { this.children = new Map(); // Map each character to a child node this.isEndOfWord = false; // Flag to indicate complete words this.suggestions = []; // Suggestions at this node (for optimization) } } class AutocompleteTrie { constructor(maxSuggestions = 5) { this.root = new TrieNode(); this.maxSuggestions = maxSuggestions; } // Insert a word into the trie insert(word) { if (!word || typeof word !== 'string') return; let node = this.root; // Travel the trie, creating nodes as needed for (const char of word.toLowerCase()) { if (!node.children.has(char)) { node.children.set(char, new TrieNode()); } node = node.children.get(char); // Add the word to suggestions at each node (for quick lookup) if (!node.suggestions.includes(word) && node.suggestions.length < this.maxSuggestions) { node.suggestions.push(word); } } node.isEndOfWord = true; } // Insert multiple words insertAll(words) { words.forEach(word => this.insert(word)); } // Get suggestions for a prefix getSuggestions(prefix) { if (!prefix || typeof prefix !== 'string') return []; let node = this.root; prefix = prefix.toLowerCase(); // Travel to the node representing the prefix for (const char of prefix) { if (!node.children.has(char)) { return []; // Prefix not found } node = node.children.get(char); } // Return stored suggestions or collect them if needed if (node.suggestions.length > 0) { return node.suggestions; } else { return this.collectWords(node, prefix, []); } } // Collect all words starting from a node (depth-first search) collectWords(node, currentPrefix, result) { // If we reached the end of a word, add it to results if (node.isEndOfWord) { result.push(currentPrefix); } // If we have enough suggestions, stop searching if (result.length >= this.maxSuggestions) { return result; } // Explore all children for (const [char, childNode] of node.children) { this.collectWords(childNode, currentPrefix + char, result); // If we have enough suggestions after exploring this child, stop if (result.length >= this.maxSuggestions) { break; } } return result; } } // Usage example - Autocomplete search input function initializeAutocomplete() { const trie = new AutocompleteTrie(); // Insert sample words trie.insertAll([ 'apple', 'application', 'append', 'algorithm', 'algebra', 'banana', 'ball', 'baseball', 'battery', 'bake', 'book', 'cat', 'car', 'cargo', 'camera', 'castle', 'celery' ]); const searchInput = document.getElementById('search-input'); const suggestionsList = document.getElementById('suggestions-list'); // Update suggestions as user types searchInput.addEventListener('input', () => { const prefix = searchInput.value.trim(); // Clear previous suggestions suggestionsList.innerHTML = ''; if (prefix.length > 0) { // Get suggestions for current prefix const suggestions = trie.getSuggestions(prefix); // Display suggestions suggestions.forEach(suggestion => { const li = document.createElement('li'); li.textContent = suggestion; li.addEventListener('click', () => { searchInput.value = suggestion; suggestionsList.innerHTML = ''; }); suggestionsList.appendChild(li); }); } }); } // This is similar to trie data structures in CS: /* class TrieNode { private Map<Character, TrieNode> children = new HashMap<>(); private boolean isEndOfWord; private List<String> suggestions = new ArrayList<>(); public void insert(String word, int index) { if (index == word.length()) { this.isEndOfWord = true; return; } char ch = word.charAt(index); TrieNode child = children.getOrDefault(ch, new TrieNode()); children.put(ch, child); if (suggestions.size() < MAX_SUGGESTIONS && !suggestions.contains(word)) { suggestions.add(word); } child.insert(word, index + 1); } public List<String> searchPrefix(String prefix, int index) { if (index == prefix.length()) { return suggestions; } char ch = prefix.charAt(index); TrieNode child = children.get(ch); if (child == null) { return Collections.emptyList(); } return child.searchPrefix(prefix, index + 1); } } */ ``` ## Web Animation and Graphics ### Canvas API - Immediate Mode Rendering **CS Concept:** 2D graphics rendering and drawing algorithms. **Web Development Example:** ```javascript // Canvas API for custom graphics rendering class ParticleSystem { constructor(canvasId, particleCount = 100) { // Get the canvas element and its context this.canvas = document.getElementById(canvasId); this.ctx = this.canvas.getContext('2d'); // Set canvas size to match display size this.resizeCanvas(); // Initialize particle array this.particles = []; this.particleCount = particleCount; // Mouse position for interaction this.mouseX = 0; this.mouseY = 0; // Setup event listeners window.addEventListener('resize', () => this.resizeCanvas()); this.canvas.addEventListener('mousemove', (e) => this.trackMouse(e)); // Create initial particles this.createParticles(); // Start animation loop this.animate(); } // Resize canvas to match display size resizeCanvas() { const { width, height } = this.canvas.getBoundingClientRect(); // Set the canvas resolution to match display this.canvas.width = width; this.canvas.height = height; // Recreate particles if canvas was already initialized if (this.particles.length > 0) { this.createParticles(); } } // Track mouse position for interactive particles trackMouse(event) { const rect = this.canvas.getBoundingClientRect(); this.mouseX = event.clientX - rect.left; this.mouseY = event.clientY - rect.top; } // Create particles createParticles() { this.particles = []; for (let i = 0; i < this.particleCount; i++) { this.particles.push({ x: Math.random() * this.canvas.width, y: Math.random() * this.canvas.height, size: Math.random() * 5 + 1, speedX: Math.random() * 2 - 1, speedY: Math.random() * 2 - 1, color: `hsl(${Math.random() * 360}, 70%, 60%)` }); } } // Update particle positions updateParticles() { for (const particle of this.particles) { // Basic movement particle.x += particle.speedX; particle.y += particle.speedY; // Mouse attraction/repulsion const dx = this.mouseX - particle.x; const dy = this.mouseY - particle.y; const distance = Math.sqrt(dx * dx + dy * dy); // Apply a small force towards/away from mouse if (distance < 100) { const forceDirectionX = dx / distance; const forceDirectionY = dy / distance; const force = (100 - distance) / 100; particle.speedX += forceDirectionX * force * 0.2; particle.speedY += forceDirectionY * force * 0.2; } // Limit speed const speed = Math.sqrt(particle.speedX * particle.speedX + particle.speedY * particle.speedY); if (speed > 2) { particle.speedX = (particle.speedX / speed) * 2; particle.speedY = (particle.speedY / speed) * 2; } // Bounce off walls if (particle.x < 0 || particle.x > this.canvas.width) { particle.speedX *= -1; } if (particle.y < 0 || particle.y > this.canvas.height) { particle.speedY *= -1; } } } // Draw all particles drawParticles() { // Clear the canvas this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); // Draw each particle for (const particle of this.particles) { this.ctx.beginPath(); this.ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2); this.ctx.fillStyle = particle.color; this.ctx.fill(); } // Draw connections between close particles this.ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)'; this.ctx.lineWidth = 0.5; for (let i = 0; i < this.particles.length; i++) { for (let j = i + 1; j < this.particles.length; j++) { const dx = this.particles[i].x - this.particles[j].x; const dy = this.particles[i].y - this.particles[j].y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < 85) { this.ctx.beginPath(); this.ctx.moveTo(this.particles[i].x, this.particles[i].y); this.ctx.lineTo(this.particles[j].x, this.particles[j].y); this.ctx.stroke(); } } } } // Animation loop animate() { this.updateParticles(); this.drawParticles(); // Continue animation requestAnimationFrame(() => this.animate()); } } // Usage example document.addEventListener('DOMContentLoaded', () => { const particleSystem = new ParticleSystem('particle-canvas', 150); }); // This is similar to graphics libraries in CS: /* class GraphicsEngine { private Canvas canvas; private List<Particle> particles; private Vector2D mousePosition; public void update(float deltaTime) { for (Particle particle : particles) { // Update position particle.position.add(particle.velocity.multiply(deltaTime)); // Apply physics Vector2D direction = mousePosition.subtract(particle.position); float distance = direction.length(); if (distance < 100) { direction.normalize(); float force = (100 - distance) / 100; particle.velocity.add(direction.multiply(force * 0.2f)); } // Collision detection if (particle.position.x < 0 || particle.position.x > canvas.width) { particle.velocity.x *= -1; } if (particle.position.y < 0 || particle.position.y > canvas.height) { particle.velocity.y *= -1; } } } public void render() { canvas.clear(); for (Particle particle : particles) { canvas.drawCircle(particle.position, particle.size, particle.color); } // Draw connections for (int i = 0; i < particles.size(); i++) { for (int j = i + 1; j < particles.size(); j++) { float distance = particles.get(i).position.distanceTo(particles.get(j).position); if (distance < 85) { canvas.drawLine(particles.get(i).position, particles.get(j).position, Color.WHITE_TRANSPARENT); } } } } } */ ``` ## Web Workers and Multithreading ### Web Workers - Browser Multithreading **CS Concept:** Parallel computation and worker threads. **Web Development Example:** ```javascript // main.js - Main thread code class ImageProcessor { constructor() { // Initialize the worker this.worker = new Worker('image-worker.js'); // Set up event listener for messages from the worker this.worker.addEventListener('message', this.handleWorkerMessage.bind(this)); } // Process an image using the worker processImage(imageData, filter) { const canvas = document.getElementById('preview-canvas'); const ctx = canvas.getContext('2d'); // Show loading indicator this.showLoading(true); // Send the image data and filter to the worker this.worker.postMessage({ type: 'processImage', imageData: imageData, filter: filter }); } // Handle messages from the worker handleWorkerMessage(event) { const { type, processedImageData, duration } = event.data; if (type === 'processComplete') { // Get the canvas const canvas = document.getElementById('preview-canvas'); const ctx = canvas.getContext('2d'); // Put the processed image data back onto the canvas ctx.putImageData(processedImageData, 0, 0); // Show processing time document.getElementById('processing-time').textContent = `Processing time: ${duration.toFixed(2)}ms`; // Hide loading indicator this.showLoading(false); } } // Toggle loading indicator showLoading(isLoading) { document.getElementById('loading-indicator').style.display = isLoading ? 'block' : 'none'; } } // Handle file upload and processing document.addEventListener('DOMContentLoaded', () => { const imageProcessor = new ImageProcessor(); const fileInput = document.getElementById('image-input'); const filterSelect = document.getElementById('filter-select'); const canvas = document.getElementById('preview-canvas'); const ctx = canvas.getContext('2d'); // When a file is selected fileInput.addEventListener('change', (event) => { const file = event.target.files[0]; if (file && file.type.match('image.*')) { const reader = new FileReader(); reader.onload = (e) => { const img = new Image(); img.onload = () => { // Resize canvas to match image canvas.width = img.width; canvas.height = img.height; // Draw the original image ctx.drawImage(img, 0, 0); // Get the image data const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); // Apply the selected filter const filter = filterSelect.value; imageProcessor.processImage(imageData, filter); }; img.src = e.target.result; }; reader.readAsDataURL(file); } }); // When filter is changed filterSelect.addEventListener('change', () => { // If we already have an image, reprocess with new filter if (canvas.width > 0 && canvas.height > 0) { const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const filter = filterSelect.value; imageProcessor.processImage(imageData, filter); } }); }); // image-worker.js - Worker thread code self.addEventListener('message', function(event) { const { type, imageData, filter } = event.data; if (type === 'processImage') { const startTime = performance.now(); // Process the image based on the selected filter const processedImageData = applyFilter(imageData, filter); const endTime = performance.now(); const duration = endTime - startTime; // Send the processed image data back to the main thread self.postMessage({ type: 'processComplete', processedImageData: processedImageData, duration: duration }); } }); // Apply different image filters function applyFilter(imageData, filter) { const { data, width, height } = imageData; const newImageData = new ImageData( new Uint8ClampedArray(data), width, height ); switch (filter) { case 'grayscale': applyGrayscale(newImageData.data); break; case 'sepia': applySepia(newImageData.data); break; case 'invert': applyInvert(newImageData.data); break; case 'blur': applyBlur(newImageData.data, width, height); break; case 'sharpen': applySharpen(newImageData.data, width, height); break; // Add more filters as needed } return newImageData; } // Filter implementations function applyGrayscale(data) { for (let i = 0; i < data.length; i += 4) { const r = data[i]; const g = data[i + 1]; const b = data[i + 2]; // Weighted average for human eye perception const gray = 0.299 * r + 0.587 * g + 0.114 * b; data[i] = gray; // Red data[i + 1] = gray; // Green data[i + 2] = gray; // Blue // Alpha channel (data[i + 3]) remains unchanged } } function applySepia(data) { for (let i = 0; i < data.length; i += 4) { const r = data[i]; const g = data[i + 1]; const b = data[i + 2]; data[i] = Math.min(255, (r * 0.393) + (g * 0.769) + (b * 0.189)); // Red data[i + 1] = Math.min(255, (r * 0.349) + (g * 0.686) + (b * 0.168)); // Green data[i + 2] = Math.min(255, (r * 0.272) + (g * 0.534) + (b * 0.131)); // Blue } } function applyInvert(data) { for (let i = 0; i < data.length; i += 4) { data[i] = 255 - data[i]; // Red data[i + 1] = 255 - data[i + 1]; // Green data[i + 2] = 255 - data[i + 2]; // Blue } } function applyBlur(data, width, height) { // Simple box blur implementation (3x3 kernel) const kernel = [ 1/9, 1/9, 1/9, 1/9, 1/9, 1/9, 1/9, 1/9, 1/9 ]; applyConvolution(data, width, height, kernel); } function applySharpen(data, width, height) { // Sharpen kernel const kernel = [ 0, -1, 0, -1, 5, -1, 0, -1, 0 ]; applyConvolution(data, width, height, kernel); } // Convolution operation (applies a kernel to the image) function applyConvolution(data, width, height, kernel) { // Create a copy of the original data const origData = new Uint8ClampedArray(data); const kernelSize = Math.sqrt(kernel.length); const kernelRadius = Math.floor(kernelSize / 2); // Apply convolution to each pixel for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { const index = (y * width + x) * 4; let r = 0, g = 0, b = 0; // Apply kernel to pixel and its neighbors for (let ky = 0; ky < kernelSize; ky++) { for (let kx = 0; kx < kernelSize; kx++) { const kernelIndex = ky * kernelSize + kx; // Calculate neighbor coordinates with edge handling const nx = Math.min(width - 1, Math.max(0, x + kx - kernelRadius)); const ny = Math.min(height - 1, Math.max(0, y + ky - kernelRadius)); // Get neighbor pixel const neighborIndex = (ny * width + nx) * 4; // Apply kernel weight r += origData[neighborIndex] * kernel[kernelIndex]; g += origData[neighborIndex + 1] * kernel[kernelIndex]; b += origData[neighborIndex + 2] * kernel[kernelIndex]; } } // Update pixel data data[index] = Math.min(255, Math.max(0, r)); data[index + 1] = Math.min(255, Math.max(0, g)); data[index + 2] = Math.min(255, Math.max(0, b)); // Alpha channel remains unchanged } } } // This is similar to multithreaded programming in CS: /* class ImageProcessor { private ExecutorService threadPool; public ImageProcessor() { this.threadPool = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors() ); } public Future<BufferedImage> processImage(BufferedImage image, String filter) { return threadPool.submit(() -> { // Apply filter in background thread return applyFilter(image, filter); }); } private BufferedImage applyFilter(BufferedImage image, String filter) { int width = image.getWidth(); int height = image.getHeight(); BufferedImage result = new BufferedImage(width, height, image.getType()); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int rgb = image.getRGB(x, y); int r = (rgb >> 16) & 0xFF; int g = (rgb >> 8) & 0xFF; int b = rgb & 0xFF; // Apply filter to pixel int[] newRgb = filterPixel(r, g, b, filter); int newColor = (newRgb[0] << 16) | (newRgb[1] << 8) | newRgb[2]; result.setRGB(x, y, newColor); } } return result; } } */ ``` ## Browser Storage - IndexedDB(Warning - dirty - proof of concept) ### IndexedDB - Client-Side Database **CS Concept:** Transactional database operations in a browser environment. **Web Development Example:** ```javascript // IndexedDB Database - Client-side database implementation class TaskDatabase { constructor(dbName = 'taskManager', version = 1) { this.dbName = dbName; this.version = version; this.db = null; } // Open the database connection async open() { return new Promise((resolve, reject) => { const request = indexedDB.open(this.dbName, this.version); // Handle database upgrade (called when creating or upgrading db) request.onupgradeneeded = (event) => { const db = event.target.result; // Create object store if it doesn't exist if (!db.objectStoreNames.contains('tasks')) { // Create the tasks object store with auto-incrementing ID const store = db.createObjectStore('tasks', { keyPath: 'id', autoIncrement: true }); // Create indexes for efficient querying store.createIndex('title', 'title', { unique: false }); store.createIndex('priority', 'priority', { unique: false }); store.createIndex('status', 'status', { unique: false }); store.createIndex('dueDate', 'dueDate', { unique: false }); } }; // Handle success request.onsuccess = (event) => { this.db = event.target.result; resolve(this.db); }; // Handle errors request.onerror = (event) => { reject(`Database error: ${event.target.error}`); }; }); } // Add a new task async addTask(task) { if (!this.db) await this.open(); return new Promise((resolve, reject) => { const transaction = this.db.transaction(['tasks'], 'readwrite'); const store = transaction.objectStore('tasks'); // Add default values if not provided const newTask = { title: task.title, description: task.description || '', priority: task.priority || 'medium', status: task.status || 'pending', dueDate: task.dueDate || new Date(), createdAt: new Date() }; const request = store.add(newTask); request.onsuccess = (event) => { // Return the inserted task with its new ID newTask.id = event.target.result; resolve(newTask); }; request.onerror = (event) => { reject(`Error adding task: ${event.target.error}`); }; // Handle transaction completion transaction.oncomplete = () => { console.log('Transaction completed: Task added'); }; // Handle transaction errors transaction.onerror = (event) => { reject(`Transaction error: ${event.target.error}`); }; }); } // Get a task by ID async getTask(id) { if (!this.db) await this.open(); return new Promise((resolve, reject) => { const transaction = this.db.transaction(['tasks'], 'readonly'); const store = transaction.objectStore('tasks'); const request = store.get(id); request.onsuccess = (event) => { resolve(event.target.result); }; request.onerror = (event) => { reject(`Error getting task: ${event.target.error}`); }; }); } // Update an existing task async updateTask(task) { if (!this.db) await this.open(); return new Promise((resolve, reject) => { const transaction = this.db.transaction(['tasks'], 'readwrite'); const store = transaction.objectStore('tasks'); // Add updatedAt timestamp task.updatedAt = new Date(); const request = store.put(task); request.onsuccess = () => { resolve(task); }; request.onerror = (event) => { reject(`Error updating task: ${event.target.error}`); }; }); } // Delete a task async deleteTask(id) { if (!this.db) await this.open(); return new Promise((resolve, reject) => { const transaction = this.db.transaction(['tasks'], 'readwrite'); const store = transaction.objectStore('tasks'); const request = store.delete(id); request.onsuccess = () => { resolve(true); }; request.onerror = (event) => { reject(`Error deleting task: ${event.target.error}`); }; }); } // Get all tasks async getAllTasks() { if (!this.db) await this.open(); return new Promise((resolve, reject) => { const transaction = this.db.transaction(['tasks'], 'readonly'); const store = transaction.objectStore('tasks'); const request = store.getAll(); request.onsuccess = (event) => { resolve(event.target.result); }; request.onerror = (event) => { reject(`Error getting tasks: ${event.target.error}`); }; }); } // Get tasks by status (using an index) async getTasksByStatus(status) { if (!this.db) await this.open(); return new Promise((resolve, reject) => { const transaction = this.db.transaction(['tasks'], 'readonly'); const store = transaction.objectStore('tasks'); const index = store.index('status'); const request = index.getAll(status); request.onsuccess = (event) => { resolve(event.target.result); }; request.onerror = (event) => { reject(`Error getting tasks by status: ${event.target.error}`); }; }); } // Get tasks due within a date range (using cursor) async getTasksDueSoon(days = 7) { if (!this.db) await this.open(); return new Promise((resolve, reject) => { const transaction = this.db.transaction(['tasks'], 'readonly'); const store = transaction.objectStore('tasks'); const index = store.index('dueDate'); // Calculate the date range const now = new Date(); const futureDate = new Date(); futureDate.setDate(now.getDate() + days); // Create a key range for the dates const dateRange = IDBKeyRange.bound(now, futureDate); // Use a cursor to iterate through the range const tasks = []; const request = index.openCursor(dateRange); request.onsuccess = (event) => { const cursor = event.target.result; if (cursor) { tasks.push(cursor.value); cursor.continue(); } else { // No more entries resolve(tasks); } }; request.onerror = (event) => { reject(`Error getting tasks due soon: ${event.target.error}`); }; }); } // Close the database connection close() { if (this.db) { this.db.close(); this.db = null; } } } // Usage example - Task Manager application async function initTaskManager() { try { const taskDB = new TaskDatabase(); await taskDB.open(); console.log('Task database opened successfully'); // DOM elements const taskForm = document.getElementById('task-form'); const taskList = document.getElementById('task-list'); const taskFilter = document.getElementById('task-filter'); // Load and display tasks await displayTasks(); // Add task form submission taskForm.addEventListener('submit', async (e) => { e.preventDefault(); const newTask = { title: taskForm.title.value, description: taskForm.description.value, priority: taskForm.priority.value, dueDate: new Date(taskForm.dueDate.value) }; try { await taskDB.addTask(newTask); taskForm.reset(); await displayTasks(); } catch (error) { console.error('Error adding task:', error); } }); // Task filter change taskFilter.addEventListener('change', async () => { await displayTasks(); }); // Display tasks based on filter async function displayTasks() { let tasks; const filter = taskFilter.value; try { if (filter === 'all') { tasks = await taskDB.getAllTasks(); } else if (filter === 'due-soon') { tasks = await taskDB.getTasksDueSoon(7); } else { // Filter by status tasks = await taskDB.getTasksByStatus(filter); } // Sort tasks by due date tasks.sort((a, b) => new Date(a.dueDate) - new Date(b.dueDate)); // Clear current list taskList.innerHTML = ''; // Add tasks to the DOM tasks.forEach(task => { const taskElement = createTaskElement(task); taskList.appendChild(taskElement); }); } catch (error) { console.error('Error displaying tasks:', error); } } // Create task DOM element function createTaskElement(task) { const taskEl = document.createElement('div'); taskEl.className = `task-item priority-${task.priority}`; taskEl.dataset.id = task.id; // Format due date const dueDate = new Date(task.dueDate); const formattedDate = dueDate.toLocaleDateString(); taskEl.innerHTML = ` <h3>${task.title}</h3> <p>${task.description}</p> <div class="task-meta"> <span class="task-priority">${task.priority}</span> <span class="task-status">${task.status}</span> <span class="task-due-date">Due: ${formattedDate}</span> </div> <div class="task-actions"> <button class="complete-btn">Complete</button> <button class="edit-btn">Edit</button> <button class="delete-btn">Delete</button> </div> `; // Add event listeners const completeBtn = taskEl.querySelector('.complete-btn'); const editBtn = taskEl.querySelector('.edit-btn'); const deleteBtn = taskEl.querySelector('.delete-btn'); completeBtn.addEventListener('click', async () => { try { const taskData = await taskDB.getTask(task.id); taskData.status = 'completed'; await taskDB.updateTask(taskData); await displayTasks(); } catch (error) { console.error('Error completing task:', error); } }); editBtn.addEventListener('click', async () => { try { const taskData = await taskDB.getTask(task.id); // Populate form for editing taskForm.title.value = taskData.title; taskForm.description.value = taskData.description; taskForm.priority.value = taskData.priority; // Format date for input field (YYYY-MM-DD) const dueDate = new Date(taskData.dueDate); const year = dueDate.getFullYear(); const month = String(dueDate.getMonth() + 1).padStart(2, '0'); const day = String(dueDate.getDate()).padStart(2, '0'); taskForm.dueDate.value = `${year}-${month}-${day}`; // Change form to update mode taskForm.dataset.mode = 'edit'; taskForm.dataset.taskId = task.id; document.querySelector('#form-submit-btn').textContent = 'Update Task'; } catch (error) { console.error('Error editing task:', error); } }); deleteBtn.addEventListener('click', async () => { if (confirm('Are you sure you want to delete this task?')) { try { await taskDB.deleteTask(task.id); await displayTasks(); } catch (error) { console.error('Error deleting task:', error); } } }); return taskEl; } } catch (error) { console.error('Failed to initialize task manager:', error); } } // Initialize app when DOM is ready document.addEventListener('DOMContentLoaded', initTaskManager); // This is similar to database operations in CS: /* public class TaskRepository { private Connection connection; public TaskRepository(Connection connection) { this.connection = connection; initializeDatabase(); } private void initializeDatabase() { try { Statement stmt = connection.createStatement(); stmt.execute( "CREATE TABLE IF NOT EXISTS tasks (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "title TEXT NOT NULL, " + "description TEXT, " + "priority TEXT, " + "status TEXT, " + "due_date TIMESTAMP, " + "created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, " + "updated_at TIMESTAMP)" ); // Create indexes stmt.execute("CREATE INDEX IF NOT EXISTS idx_status ON tasks(status)"); stmt.execute("CREATE INDEX IF NOT EXISTS idx_due_date ON tasks(due_date)"); } catch (SQLException e) { throw new RuntimeException("Failed to initialize database", e); } } public Task addTask(Task task) { try { PreparedStatement stmt = connection.prepareStatement( "INSERT INTO tasks (title, description, priority, status, due_date) " + "VALUES (?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS ); stmt.setString(1, task.getTitle()); stmt.setString(2, task.getDescription()); stmt.setString(3, task.getPriority()); stmt.setString(4, task.getStatus()); stmt.setTimestamp(5, new Timestamp(task.getDueDate().getTime())); int affectedRows = stmt.executeUpdate(); if (affectedRows == 0) { throw new SQLException("Creating task failed, no rows affected."); } ResultSet generatedKeys = stmt.getGeneratedKeys(); if (generatedKeys.next()) { task.setId(generatedKeys.getLong(1)); } else { throw new SQLException("Creating task failed, no ID obtained."); } return task; } catch (SQLException e) { throw new RuntimeException("Failed to add task", e); } } public List<Task> getTasksByStatus(String status) { List<Task> tasks = new ArrayList<>(); try { PreparedStatement stmt = connection.prepareStatement( "SELECT * FROM tasks WHERE status = ?" ); stmt.setString(1, status); ResultSet rs = stmt.executeQuery(); while (rs.next()) { Task task = new Task(); task.setId(rs.getLong("id")); task.setTitle(rs.getString("title")); task.setDescription(rs.getString("description")); task.setPriority(rs.getString("priority")); task.setStatus(rs.getString("status")); task.setDueDate(rs.getTimestamp("due_date")); tasks.add(task); } } catch (SQLException e) { throw new RuntimeException("Failed to get tasks by status", e); } return tasks; } } */ ```