Morten Sæther

← Blog

Episode 21: Advanced JavaScript Development

25 May 2026

Mastering Modern JavaScript Patterns for Enterprise Power Pages

Introduction

Professional Power Pages development requires sophisticated JavaScript implementations that go far beyond basic scripting. This comprehensive episode covers modern JavaScript patterns, ES6+ features, advanced async programming, modular architecture, and enterprise-grade development practices. You’ll learn to build robust, maintainable, and scalable JavaScript solutions that integrate seamlessly with Power Pages while following industry best practices.

Key Concepts

Learning Objectives

By the end of this episode, you will be able to:

Why Advanced JavaScript Development Matters

Modern Power Pages require sophisticated client-side functionality that demands professional JavaScript development skills. Advanced JavaScript enables rich user experiences, complex business logic implementation, efficient data handling, and seamless integration with external services while maintaining code quality, performance, and maintainability.

Step-by-Step Guide

1. Modern JavaScript Patterns and ES6+ Features

Advanced Class-Based Architecture:

// Modern JavaScript Class with Advanced Features
class AdvancedPortalManager {
    // Private fields (ES2022)
    #apiClient;
    #eventBus;
    #configuration;
    #performanceMonitor;
    
    // Static properties
    static VERSION = '2.1.0';
    static SUPPORTED_FEATURES = new Set(['async', 'modules', 'workers', 'cache']);
    
    constructor(config = {}) {
        // Destructuring with defaults
        const {
            apiEndpoint = '/api/v2',
            enableCache = true,
            performanceTracking = true,
            eventBusEnabled = true,
            ...additionalConfig
        } = config;
        
        this.#configuration = {
            apiEndpoint,
            enableCache,
            performanceTracking,
            eventBusEnabled,
            ...additionalConfig
        };
        
        // Initialize private components
        this.#apiClient = new APIClient(this.#configuration);
        this.#eventBus = eventBusEnabled ? new EventBus() : null;
        this.#performanceMonitor = performanceTracking ? new PerformanceMonitor() : null;
        
        // Bind methods to maintain context
        this.handleUserAction = this.handleUserAction.bind(this);
        this.processData = this.processData.bind(this);
        
        this.initialize();
    }
    
    // Async initialization with error handling
    async initialize() {
        try {
            await this.#loadConfiguration();
            await this.#setupEventListeners();
            await this.#initializeComponents();
            
            this.#eventBus?.emit('portal:initialized', {
                version: AdvancedPortalManager.VERSION,
                timestamp: new Date().toISOString()
            });
            
        } catch (error) {
            console.error('Portal initialization failed:', error);
            await this.#handleInitializationError(error);
        }
    }
    
    // Advanced method with multiple parameter patterns
    async fetchData(resource, options = {}) {
        // Parameter destructuring with validation
        const {
            filters = {},
            pagination = { page: 1, limit: 10 },
            include = [],
            cache = this.#configuration.enableCache,
            timeout = 30000,
            retries = 3
        } = options;
        
        // Input validation using modern JavaScript
        if (!resource || typeof resource !== 'string') {
            throw new TypeError('Resource parameter must be a non-empty string');
        }
        
        // Performance tracking
        const performanceId = this.#performanceMonitor?.startOperation('fetchData', { resource });
        
        try {
            // Build query parameters using URLSearchParams
            const queryParams = new URLSearchParams();
            
            // Add filters
            Object.entries(filters).forEach(([key, value]) => {
                if (Array.isArray(value)) {
                    value.forEach(v => queryParams.append(`${key}[]`, v));
                } else {
                    queryParams.set(key, value);
                }
            });
            
            // Add pagination
            queryParams.set('page', pagination.page.toString());
            queryParams.set('limit', pagination.limit.toString());
            
            // Add includes
            if (include.length > 0) {
                queryParams.set('include', include.join(','));
            }
            
            // Execute request with retry logic
            const result = await this.#executeWithRetry(async () => {
                return await this.#apiClient.get(`/${resource}?${queryParams.toString()}`, {
                    timeout,
                    cache
                });
            }, retries);
            
            // Transform and validate response
            const processedData = await this.#processApiResponse(result, resource);
            
            // Emit success event
            this.#eventBus?.emit('data:fetched', {
                resource,
                count: processedData.length,
                timestamp: new Date().toISOString()
            });
            
            return processedData;
            
        } catch (error) {
            // Enhanced error handling
            const enhancedError = this.#enhanceError(error, 'fetchData', { resource, options });
            this.#eventBus?.emit('data:fetchError', enhancedError);
            throw enhancedError;
            
        } finally {
            this.#performanceMonitor?.endOperation(performanceId);
        }
    }
    
    // Advanced data processing with functional programming
    async processData(data, processors = []) {
        // Default processors using arrow functions and advanced patterns
        const defaultProcessors = [
            // Data validation processor
            (items) => items.filter(item => this.#validateDataItem(item)),
            
            // Data enrichment processor
            async (items) => await Promise.all(
                items.map(async item => ({
                    ...item,
                    processedAt: new Date().toISOString(),
                    enrichmentData: await this.#enrichDataItem(item)
                }))
            ),
            
            // Data transformation processor
            (items) => items.map(item => this.#transformDataItem(item)),
            
            // Data sorting processor
            (items) => items.sort((a, b) => 
                new Date(b.createdAt) - new Date(a.createdAt)
            )
        ];
        
        const allProcessors = [...defaultProcessors, ...processors];
        
        // Process data through pipeline using reduce
        return await allProcessors.reduce(async (accPromise, processor) => {
            const acc = await accPromise;
            return await processor(acc);
        }, Promise.resolve(data));
    }
    
    // Advanced event handling with delegation and modern patterns
    #setupEventListeners() {
        // Use modern event delegation
        document.addEventListener('click', this.#handleGlobalClick.bind(this));
        document.addEventListener('submit', this.#handleGlobalSubmit.bind(this));
        
        // Custom event listeners with cleanup
        this.#eventBus?.on('user:action', this.handleUserAction);
        this.#eventBus?.on('data:changed', this.#handleDataChange.bind(this));
        
        // Setup cleanup on page unload
        window.addEventListener('beforeunload', () => {
            this.#cleanup();
        });
    }
    
    #handleGlobalClick(event) {
        // Modern event delegation using closest()
        const actionElement = event.target.closest('[data-action]');
        if (!actionElement) return;
        
        const action = actionElement.dataset.action;
        const config = this.#parseActionConfig(actionElement);
        
        // Prevent default if needed
        if (config.preventDefault) {
            event.preventDefault();
        }
        
        // Execute action with error boundary
        this.#executeAction(action, config, event).catch(error => {
            console.error(`Action ${action} failed:`, error);
            this.#showUserError(`Failed to execute ${action}`);
        });
    }
    
    // Advanced async action execution
    async #executeAction(actionName, config, event) {
        const actionMap = new Map([
            ['fetchRecords', () => this.#handleFetchRecords(config)],
            ['saveRecord', () => this.#handleSaveRecord(config)],
            ['deleteRecord', () => this.#handleDeleteRecord(config)],
            ['exportData', () => this.#handleExportData(config)],
            ['importData', () => this.#handleImportData(config)],
            ['validateForm', () => this.#handleValidateForm(config)]
        ]);
        
        const actionHandler = actionMap.get(actionName);
        if (!actionHandler) {
            throw new Error(`Unknown action: ${actionName}`);
        }
        
        // Execute with performance tracking
        const startTime = performance.now();
        try {
            const result = await actionHandler();
            const duration = performance.now() - startTime;
            
            this.#performanceMonitor?.recordAction(actionName, duration, true);
            return result;
            
        } catch (error) {
            const duration = performance.now() - startTime;
            this.#performanceMonitor?.recordAction(actionName, duration, false);
            throw error;
        }
    }
    
    // Modern async error handling and retry logic
    async #executeWithRetry(operation, retries = 3, delay = 1000) {
        let lastError;
        
        for (let attempt = 1; attempt <= retries; attempt++) {
            try {
                return await operation();
            } catch (error) {
                lastError = error;
                
                // Don't retry on certain error types
                if (this.#isNonRetryableError(error) || attempt === retries) {
                    throw error;
                }
                
                // Exponential backoff delay
                const backoffDelay = delay * Math.pow(2, attempt - 1);
                await this.#sleep(backoffDelay);
                
                console.warn(`Operation failed, retrying (${attempt}/${retries}):`, error.message);
            }
        }
        
        throw lastError;
    }
    
    // Utility method using Promise-based sleep
    #sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
    
    // Advanced error enhancement with context
    #enhanceError(originalError, operation, context = {}) {
        const enhancedError = new Error(
            `${operation} failed: ${originalError.message}`
        );
        
        // Preserve original error properties
        enhancedError.name = `Enhanced${originalError.name}`;
        enhancedError.originalError = originalError;
        enhancedError.operation = operation;
        enhancedError.context = context;
        enhancedError.timestamp = new Date().toISOString();
        enhancedError.stack = originalError.stack;
        
        // Add debugging information
        enhancedError.debugInfo = {
            userAgent: navigator.userAgent,
            url: window.location.href,
            timestamp: Date.now(),
            sessionId: this.#getSessionId(),
            ...context
        };
        
        return enhancedError;
    }
    
    // Memory management and cleanup
    #cleanup() {
        // Remove event listeners
        this.#eventBus?.removeAllListeners();
        
        // Clear caches
        this.#apiClient?.clearCache();
        
        // Cancel pending operations
        this.#performanceMonitor?.cleanup();
        
        // Clear references
        this.#apiClient = null;
        this.#eventBus = null;
        this.#performanceMonitor = null;
    }
    
    // Public method for manual cleanup
    destroy() {
        this.#cleanup();
    }
    
    // Getter methods for controlled access to private data
    get configuration() {
        // Return a deep copy to prevent external modification
        return JSON.parse(JSON.stringify(this.#configuration));
    }
    
    get isInitialized() {
        return !!(this.#apiClient && this.#eventBus);
    }
    
    // Static factory method
    static create(config) {
        return new AdvancedPortalManager(config);
    }
    
    // Static utility methods
    static isSupported() {
        return AdvancedPortalManager.SUPPORTED_FEATURES.size > 0 &&
               'Promise' in window &&
               'fetch' in window;
    }
}

Advanced Module System and Dependency Management:

// ES6 Module with Advanced Exports
// utils/dataProcessor.js
export class DataProcessor {
    static #transforms = new Map();
    
    // Register custom transform functions
    static registerTransform(name, transformFn) {
        if (typeof transformFn !== 'function') {
            throw new TypeError('Transform must be a function');
        }
        this.#transforms.set(name, transformFn);
    }
    
    // Apply transforms in sequence
    static async applyTransforms(data, transformNames = []) {
        return await transformNames.reduce(async (accPromise, transformName) => {
            const acc = await accPromise;
            const transform = this.#transforms.get(transformName);
            
            if (!transform) {
                throw new Error(`Transform '${transformName}' not found`);
            }
            
            return await transform(acc);
        }, Promise.resolve(data));
    }
}

// Default transforms
DataProcessor.registerTransform('normalize', (data) => {
    return Array.isArray(data) ? data : [data];
});

DataProcessor.registerTransform('validate', (data) => {
    return data.filter(item => item && typeof item === 'object');
});

DataProcessor.registerTransform('enrich', async (data) => {
    return Promise.all(data.map(async item => ({
        ...item,
        id: item.id || crypto.randomUUID(),
        timestamp: new Date().toISOString()
    })));
});

export default DataProcessor;

// Advanced API Client Module
// services/apiClient.js
export class APIClient {
    #baseURL;
    #defaultHeaders;
    #cache;
    #requestInterceptors;
    #responseInterceptors;
    
    constructor(config = {}) {
        this.#baseURL = config.baseURL || '';
        this.#defaultHeaders = {
            'Content-Type': 'application/json',
            ...config.headers
        };
        this.#cache = new Map();
        this.#requestInterceptors = [];
        this.#responseInterceptors = [];
    }
    
    // Interceptor methods for middleware pattern
    addRequestInterceptor(interceptor) {
        this.#requestInterceptors.push(interceptor);
        return () => {
            const index = this.#requestInterceptors.indexOf(interceptor);
            if (index > -1) {
                this.#requestInterceptors.splice(index, 1);
            }
        };
    }
    
    addResponseInterceptor(interceptor) {
        this.#responseInterceptors.push(interceptor);
        return () => {
            const index = this.#responseInterceptors.indexOf(interceptor);
            if (index > -1) {
                this.#responseInterceptors.splice(index, 1);
            }
        };
    }
    
    // Advanced request method with full HTTP support
    async request(method, endpoint, options = {}) {
        const {
            data,
            headers = {},
            cache = false,
            timeout = 30000,
            signal,
            ...fetchOptions
        } = options;
        
        // Build full URL
        const url = new URL(endpoint, this.#baseURL).toString();
        
        // Check cache first
        if (cache && method === 'GET') {
            const cached = this.#cache.get(url);
            if (cached && Date.now() < cached.expires) {
                return cached.data;
            }
        }
        
        // Prepare request config
        let requestConfig = {
            method,
            headers: { ...this.#defaultHeaders, ...headers },
            signal: signal || AbortSignal.timeout(timeout),
            ...fetchOptions
        };
        
        // Add body for non-GET requests
        if (data && !['GET', 'HEAD'].includes(method)) {
            requestConfig.body = JSON.stringify(data);
        }
        
        // Apply request interceptors
        for (const interceptor of this.#requestInterceptors) {
            requestConfig = await interceptor(requestConfig) || requestConfig;
        }
        
        try {
            // Execute request
            const response = await fetch(url, requestConfig);
            
            // Apply response interceptors
            let processedResponse = response;
            for (const interceptor of this.#responseInterceptors) {
                processedResponse = await interceptor(processedResponse) || processedResponse;
            }
            
            // Handle response
            if (!processedResponse.ok) {
                throw new HTTPError(
                    `HTTP ${processedResponse.status}: ${processedResponse.statusText}`,
                    processedResponse.status,
                    processedResponse
                );
            }
            
            // Parse response
            const responseData = await this.#parseResponse(processedResponse);
            
            // Cache successful GET requests
            if (cache && method === 'GET') {
                this.#cache.set(url, {
                    data: responseData,
                    expires: Date.now() + (cache === true ? 300000 : cache) // 5 min default
                });
            }
            
            return responseData;
            
        } catch (error) {
            if (error.name === 'AbortError') {
                throw new TimeoutError(`Request timeout after ${timeout}ms`);
            }
            throw error;
        }
    }
    
    // Convenience methods
    get(endpoint, options = {}) {
        return this.request('GET', endpoint, options);
    }
    
    post(endpoint, data, options = {}) {
        return this.request('POST', endpoint, { ...options, data });
    }
    
    put(endpoint, data, options = {}) {
        return this.request('PUT', endpoint, { ...options, data });
    }
    
    patch(endpoint, data, options = {}) {
        return this.request('PATCH', endpoint, { ...options, data });
    }
    
    delete(endpoint, options = {}) {
        return this.request('DELETE', endpoint, options);
    }
    
    // Advanced batch request method
    async batch(requests) {
        const results = await Promise.allSettled(
            requests.map(({ method, endpoint, ...options }) => 
                this.request(method, endpoint, options)
            )
        );
        
        return results.map((result, index) => ({
            request: requests[index],
            success: result.status === 'fulfilled',
            data: result.status === 'fulfilled' ? result.value : null,
            error: result.status === 'rejected' ? result.reason : null
        }));
    }
    
    // Response parsing with content type detection
    async #parseResponse(response) {
        const contentType = response.headers.get('content-type') || '';
        
        if (contentType.includes('application/json')) {
            return await response.json();
        } else if (contentType.includes('text/')) {
            return await response.text();
        } else if (contentType.includes('application/octet-stream')) {
            return await response.arrayBuffer();
        } else {
            return await response.blob();
        }
    }
    
    clearCache() {
        this.#cache.clear();
    }
}

// Custom error classes
export class HTTPError extends Error {
    constructor(message, status, response) {
        super(message);
        this.name = 'HTTPError';
        this.status = status;
        this.response = response;
    }
}

export class TimeoutError extends Error {
    constructor(message) {
        super(message);
        this.name = 'TimeoutError';
    }
}

2. Advanced Asynchronous Programming Patterns

Reactive Programming with Observables:

// Advanced Observable Pattern Implementation
class Observable {
    constructor(subscribeFn) {
        this._subscribeFn = subscribeFn;
    }
    
    subscribe(observer) {
        const subscription = this._subscribeFn(observer);
        return {
            unsubscribe: subscription?.unsubscribe || (() => {})
        };
    }
    
    // Transformation operators
    map(transformFn) {
        return new Observable(observer => {
            return this.subscribe({
                next: value => observer.next(transformFn(value)),
                error: error => observer.error(error),
                complete: () => observer.complete()
            });
        });
    }
    
    filter(predicateFn) {
        return new Observable(observer => {
            return this.subscribe({
                next: value => {
                    if (predicateFn(value)) {
                        observer.next(value);
                    }
                },
                error: error => observer.error(error),
                complete: () => observer.complete()
            });
        });
    }
    
    debounce(delay) {
        return new Observable(observer => {
            let timeoutId;
            
            return this.subscribe({
                next: value => {
                    clearTimeout(timeoutId);
                    timeoutId = setTimeout(() => observer.next(value), delay);
                },
                error: error => observer.error(error),
                complete: () => observer.complete()
            });
        });
    }
    
    throttle(delay) {
        return new Observable(observer => {
            let lastEmit = 0;
            
            return this.subscribe({
                next: value => {
                    const now = Date.now();
                    if (now - lastEmit >= delay) {
                        lastEmit = now;
                        observer.next(value);
                    }
                },
                error: error => observer.error(error),
                complete: () => observer.complete()
            });
        });
    }
    
    // Combination operators
    static merge(...observables) {
        return new Observable(observer => {
            const subscriptions = observables.map(obs => 
                obs.subscribe(observer)
            );
            
            return {
                unsubscribe: () => subscriptions.forEach(sub => sub.unsubscribe())
            };
        });
    }
    
    static combineLatest(...observables) {
        return new Observable(observer => {
            const values = new Array(observables.length);
            const hasValue = new Array(observables.length).fill(false);
            let completedCount = 0;
            
            const subscriptions = observables.map((obs, index) => 
                obs.subscribe({
                    next: value => {
                        values[index] = value;
                        hasValue[index] = true;
                        
                        if (hasValue.every(Boolean)) {
                            observer.next([...values]);
                        }
                    },
                    error: error => observer.error(error),
                    complete: () => {
                        completedCount++;
                        if (completedCount === observables.length) {
                            observer.complete();
                        }
                    }
                })
            );
            
            return {
                unsubscribe: () => subscriptions.forEach(sub => sub.unsubscribe())
            };
        });
    }
    
    // Factory methods
    static fromEvent(element, eventName) {
        return new Observable(observer => {
            const handler = event => observer.next(event);
            element.addEventListener(eventName, handler);
            
            return {
                unsubscribe: () => element.removeEventListener(eventName, handler)
            };
        });
    }
    
    static fromPromise(promise) {
        return new Observable(observer => {
            promise
                .then(value => {
                    observer.next(value);
                    observer.complete();
                })
                .catch(error => observer.error(error));
        });
    }
    
    static interval(delay) {
        return new Observable(observer => {
            let count = 0;
            const intervalId = setInterval(() => {
                observer.next(count++);
            }, delay);
            
            return {
                unsubscribe: () => clearInterval(intervalId)
            };
        });
    }
}

// Advanced Async Controller for Power Pages
class AsyncController {
    constructor() {
        this.activeOperations = new Map();
        this.operationQueue = [];
        this.maxConcurrent = 5;
        this.defaultTimeout = 30000;
    }
    
    // Execute async operation with advanced control
    async execute(operationId, asyncFn, options = {}) {
        const {
            timeout = this.defaultTimeout,
            retries = 3,
            priority = 'normal',
            cancelPrevious = false,
            dependencies = []
        } = options;
        
        // Cancel previous operation if requested
        if (cancelPrevious && this.activeOperations.has(operationId)) {
            this.cancel(operationId);
        }
        
        // Wait for dependencies
        if (dependencies.length > 0) {
            await this.waitForOperations(dependencies);
        }
        
        // Queue operation if at max capacity
        if (this.activeOperations.size >= this.maxConcurrent) {
            await this.queueOperation(operationId, asyncFn, options);
            return;
        }
        
        return this.executeImmediate(operationId, asyncFn, { timeout, retries, priority });
    }
    
    async executeImmediate(operationId, asyncFn, options) {
        const { timeout, retries, priority } = options;
        
        // Create abort controller for cancellation
        const abortController = new AbortController();
        const timeoutId = setTimeout(() => abortController.abort(), timeout);
        
        const operation = {
            id: operationId,
            abortController,
            timeoutId,
            priority,
            startTime: Date.now()
        };
        
        this.activeOperations.set(operationId, operation);
        
        try {
            // Execute with retry logic
            const result = await this.executeWithRetry(
                () => asyncFn(abortController.signal),
                retries,
                abortController.signal
            );
            
            return result;
            
        } catch (error) {
            if (error.name === 'AbortError') {
                throw new Error(`Operation ${operationId} was cancelled`);
            }
            throw error;
            
        } finally {
            // Cleanup
            clearTimeout(timeoutId);
            this.activeOperations.delete(operationId);
            
            // Process queued operations
            this.processQueue();
        }
    }
    
    async executeWithRetry(operation, retries, signal) {
        let lastError;
        
        for (let attempt = 1; attempt <= retries; attempt++) {
            if (signal.aborted) {
                throw new DOMException('Operation aborted', 'AbortError');
            }
            
            try {
                return await operation();
            } catch (error) {
                lastError = error;
                
                if (attempt === retries || signal.aborted) {
                    throw error;
                }
                
                // Exponential backoff
                const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
                await this.sleep(delay, signal);
            }
        }
        
        throw lastError;
    }
    
    async queueOperation(operationId, asyncFn, options) {
        return new Promise((resolve, reject) => {
            this.operationQueue.push({
                id: operationId,
                asyncFn,
                options,
                resolve,
                reject
            });
            
            // Sort queue by priority
            this.operationQueue.sort((a, b) => {
                const priorities = { high: 3, normal: 2, low: 1 };
                return priorities[b.options.priority || 'normal'] - 
                       priorities[a.options.priority || 'normal'];
            });
        });
    }
    
    async processQueue() {
        while (this.operationQueue.length > 0 && 
               this.activeOperations.size < this.maxConcurrent) {
            
            const queuedOperation = this.operationQueue.shift();
            const { id, asyncFn, options, resolve, reject } = queuedOperation;
            
            try {
                const result = await this.executeImmediate(id, asyncFn, options);
                resolve(result);
            } catch (error) {
                reject(error);
            }
        }
    }
    
    cancel(operationId) {
        const operation = this.activeOperations.get(operationId);
        if (operation) {
            operation.abortController.abort();
            return true;
        }
        
        // Remove from queue if not yet started
        const queueIndex = this.operationQueue.findIndex(op => op.id === operationId);
        if (queueIndex !== -1) {
            const queuedOperation = this.operationQueue.splice(queueIndex, 1)[0];
            queuedOperation.reject(new Error(`Operation ${operationId} was cancelled`));
            return true;
        }
        
        return false;
    }
    
    cancelAll() {
        // Cancel active operations
        for (const [id, operation] of this.activeOperations) {
            operation.abortController.abort();
        }
        
        // Cancel queued operations
        while (this.operationQueue.length > 0) {
            const queuedOperation = this.operationQueue.shift();
            queuedOperation.reject(new Error('All operations cancelled'));
        }
    }
    
    async waitForOperations(operationIds) {
        const checkInterval = 100;
        
        while (operationIds.some(id => this.activeOperations.has(id))) {
            await this.sleep(checkInterval);
        }
    }
    
    sleep(ms, signal) {
        return new Promise((resolve, reject) => {
            const timeoutId = setTimeout(resolve, ms);
            
            if (signal) {
                signal.addEventListener('abort', () => {
                    clearTimeout(timeoutId);
                    reject(new DOMException('Sleep aborted', 'AbortError'));
                });
            }
        });
    }
    
    getStatus() {
        return {
            activeOperations: this.activeOperations.size,
            queuedOperations: this.operationQueue.length,
            operations: Array.from(this.activeOperations.entries()).map(([id, op]) => ({
                id,
                priority: op.priority,
                duration: Date.now() - op.startTime
            }))
        };
    }
}

3. Component-Based Architecture

Advanced Component System:

// Base Component Class with Advanced Features
class Component {
    static #registry = new Map();
    static #globalMixins = [];
    
    #element;
    #props;
    #state;
    #subscriptions;
    #childComponents;
    #isDestroyed;
    
    constructor(element, props = {}) {
        this.#element = element;
        this.#props = { ...props };
        this.#state = {};
        this.#subscriptions = [];
        this.#childComponents = new Set();
        this.#isDestroyed = false;
        
        // Apply global mixins
        Component.#globalMixins.forEach(mixin => {
            Object.assign(this, mixin);
        });
        
        // Initialize component
        this.created();
        this.render();
        this.mounted();
    }
    
    // Lifecycle methods (to be overridden)
    created() {}
    mounted() {}
    updated() {}
    destroyed() {}
    
    // State management
    setState(newState, callback) {
        if (this.#isDestroyed) return;
        
        const prevState = { ...this.#state };
        this.#state = { ...this.#state, ...newState };
        
        // Trigger update
        this.updated(prevState, this.#state);
        this.render();
        
        // Execute callback after update
        if (callback) {
            requestAnimationFrame(callback);
        }
    }
    
    getState() {
        return { ...this.#state };
    }
    
    // Props management
    setProps(newProps) {
        if (this.#isDestroyed) return;
        
        const prevProps = { ...this.#props };
        this.#props = { ...this.#props, ...newProps };
        
        this.propsChanged(prevProps, this.#props);
        this.render();
    }
    
    getProps() {
        return { ...this.#props };
    }
    
    propsChanged(prevProps, newProps) {}
    
    // Event system
    emit(eventName, detail = {}) {
        const event = new CustomEvent(eventName, {
            detail: { component: this, ...detail },
            bubbles: true,
            cancelable: true
        });
        
        this.#element.dispatchEvent(event);
        return event;
    }
    
    on(eventName, handler, options = {}) {
        this.#element.addEventListener(eventName, handler, options);
        
        // Track subscription for cleanup
        const cleanup = () => this.#element.removeEventListener(eventName, handler);
        this.#subscriptions.push(cleanup);
        
        return cleanup;
    }
    
    // Child component management
    createChildComponent(ComponentClass, element, props) {
        const child = new ComponentClass(element, props);
        this.#childComponents.add(child);
        
        return child;
    }
    
    destroyChildComponent(child) {
        if (this.#childComponents.has(child)) {
            child.destroy();
            this.#childComponents.delete(child);
        }
    }
    
    // DOM utilities
    $(selector) {
        return this.#element.querySelector(selector);
    }
    
    $$(selector) {
        return Array.from(this.#element.querySelectorAll(selector));
    }
    
    // Template rendering (to be overridden)
    render() {
        // Default implementation - override in subclasses
    }
    
    // Cleanup
    destroy() {
        if (this.#isDestroyed) return;
        
        this.#isDestroyed = true;
        
        // Destroy child components
        this.#childComponents.forEach(child => child.destroy());
        this.#childComponents.clear();
        
        // Clean up subscriptions
        this.#subscriptions.forEach(cleanup => cleanup());
        this.#subscriptions = [];
        
        // Call lifecycle method
        this.destroyed();
        
        // Remove from parent if exists
        if (this.parent) {
            this.parent.destroyChildComponent(this);
        }
        
        // Clear references
        this.#element = null;
        this.#props = null;
        this.#state = null;
    }
    
    // Static registry methods
    static register(name, ComponentClass) {
        this.#registry.set(name, ComponentClass);
    }
    
    static create(name, element, props) {
        const ComponentClass = this.#registry.get(name);
        if (!ComponentClass) {
            throw new Error(`Component '${name}' not found in registry`);
        }
        
        return new ComponentClass(element, props);
    }
    
    static addGlobalMixin(mixin) {
        this.#globalMixins.push(mixin);
    }
    
    // Auto-initialization from DOM
    static initializeFromDOM() {
        document.querySelectorAll('[data-component]').forEach(element => {
            const componentName = element.dataset.component;
            const props = element.dataset.props ? JSON.parse(element.dataset.props) : {};
            
            try {
                this.create(componentName, element, props);
            } catch (error) {
                console.error(`Failed to initialize component '${componentName}':`, error);
            }
        });
    }
}

// Advanced Form Component Example
class AdvancedFormComponent extends Component {
    created() {
        this.validators = new Map();
        this.errors = new Map();
        this.validationRules = this.getProps().validationRules || {};
        
        this.setupValidation();
    }
    
    mounted() {
        // Set up form event listeners
        this.on('submit', this.handleSubmit.bind(this));
        this.on('input', this.handleInput.bind(this));
        this.on('blur', this.handleBlur.bind(this), true); // Use capture
        
        // Initialize field validation
        this.initializeFields();
    }
    
    setupValidation() {
        // Register built-in validators
        this.registerValidator('required', (value) => {
            return value != null && value !== '';
        });
        
        this.registerValidator('email', (value) => {
            const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
            return !value || emailRegex.test(value);
        });
        
        this.registerValidator('minLength', (value, min) => {
            return !value || value.length >= min;
        });
        
        this.registerValidator('maxLength', (value, max) => {
            return !value || value.length <= max;
        });
        
        this.registerValidator('pattern', (value, pattern) => {
            const regex = new RegExp(pattern);
            return !value || regex.test(value);
        });
    }
    
    registerValidator(name, validatorFn) {
        this.validators.set(name, validatorFn);
    }
    
    async handleSubmit(event) {
        event.preventDefault();
        
        const isValid = await this.validateAll();
        
        if (isValid) {
            const formData = this.getFormData();
            
            try {
                await this.submitForm(formData);
                this.emit('form:success', { data: formData });
            } catch (error) {
                this.emit('form:error', { error });
                this.showError('Submission failed. Please try again.');
            }
        } else {
            this.emit('form:invalid', { errors: this.errors });
            this.focusFirstError();
        }
    }
    
    async validateAll() {
        const fields = this.$$('[data-validate]');
        const validationPromises = fields.map(field => this.validateField(field));
        
        await Promise.all(validationPromises);
        
        return this.errors.size === 0;
    }
    
    async validateField(field) {
        const value = field.value;
        const rules = field.dataset.validate.split('|');
        const fieldName = field.name || field.id;
        
        // Clear previous errors
        this.clearFieldError(fieldName);
        
        for (const rule of rules) {
            const [ruleName, ...params] = rule.split(':');
            const validator = this.validators.get(ruleName);
            
            if (!validator) {
                console.warn(`Validator '${ruleName}' not found`);
                continue;
            }
            
            const isValid = await validator(value, ...params);
            
            if (!isValid) {
                const errorMessage = this.getErrorMessage(fieldName, ruleName, params);
                this.setFieldError(fieldName, errorMessage);
                this.showFieldError(field, errorMessage);
                break;
            }
        }
    }
    
    getFormData() {
        const formData = new FormData(this.#element);
        const data = {};
        
        for (const [key, value] of formData.entries()) {
            // Handle multiple values (checkboxes, multi-select)
            if (data[key]) {
                if (Array.isArray(data[key])) {
                    data[key].push(value);
                } else {
                    data[key] = [data[key], value];
                }
            } else {
                data[key] = value;
            }
        }
        
        return data;
    }
    
    async submitForm(data) {
        const submitUrl = this.#element.action || this.getProps().submitUrl;
        const method = this.#element.method || 'POST';
        
        const response = await fetch(submitUrl, {
            method,
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(data)
        });
        
        if (!response.ok) {
            throw new Error(`HTTP ${response.status}: ${response.statusText}`);
        }
        
        return await response.json();
    }
    
    setFieldError(fieldName, message) {
        this.errors.set(fieldName, message);
    }
    
    clearFieldError(fieldName) {
        this.errors.delete(fieldName);
    }
    
    showFieldError(field, message) {
        // Remove existing error display
        const existingError = field.parentNode.querySelector('.field-error');
        if (existingError) {
            existingError.remove();
        }
        
        // Create error element
        const errorElement = document.createElement('div');
        errorElement.className = 'field-error';
        errorElement.textContent = message;
        
        // Insert after field
        field.parentNode.insertBefore(errorElement, field.nextSibling);
        
        // Add error class to field
        field.classList.add('error');
    }
    
    getErrorMessage(fieldName, ruleName, params) {
        const messages = {
            required: `${fieldName} is required`,
            email: `Please enter a valid email address`,
            minLength: `${fieldName} must be at least ${params[0]} characters`,
            maxLength: `${fieldName} must not exceed ${params[0]} characters`,
            pattern: `${fieldName} format is invalid`
        };
        
        return messages[ruleName] || `${fieldName} is invalid`;
    }
}

// Register components
Component.register('advanced-form', AdvancedFormComponent);

Practical Example: Complete Modern JavaScript Application

Let’s build a comprehensive data management interface using all the advanced patterns:

// Modern Power Pages Data Manager
class PowerPagesDataManager {
    constructor(config = {}) {
        this.config = {
            apiEndpoint: '/api/data',
            enableRealTime: true,
            cacheStrategy: 'aggressive',
            batchSize: 100,
            ...config
        };
        
        // Initialize core services
        this.apiClient = new APIClient({ baseURL: this.config.apiEndpoint });
        this.asyncController = new AsyncController();
        this.eventBus = new EventBus();
        this.componentManager = new ComponentManager();
        
        // Set up real-time updates
        if (this.config.enableRealTime) {
            this.setupRealTimeUpdates();
        }
        
        this.initialize();
    }
    
    async initialize() {
        try {
            // Load initial configuration
            await this.loadConfiguration();
            
            // Initialize components
            await this.initializeComponents();
            
            // Set up event listeners
            this.setupEventListeners();
            
            // Load initial data
            await this.loadInitialData();
            
            console.log('PowerPagesDataManager initialized successfully');
            
        } catch (error) {
            console.error('Initialization failed:', error);
            this.handleInitializationError(error);
        }
    }
    
    async loadConfiguration() {
        const config = await this.apiClient.get('/config');
        this.config = { ...this.config, ...config };
    }
    
    async initializeComponents() {
        // Initialize data table component
        const tableElement = document.querySelector('#data-table');
        if (tableElement) {
            this.dataTable = Component.create('data-table', tableElement, {
                columns: this.config.tableColumns,
                pageSize: this.config.pageSize,
                sortable: true,
                filterable: true
            });
        }
        
        // Initialize form components
        document.querySelectorAll('.advanced-form').forEach(formElement => {
            Component.create('advanced-form', formElement, {
                validationRules: this.config.validationRules,
                submitUrl: this.config.submitUrl
            });
        });
    }
    
    setupEventListeners() {
        // Listen for data changes
        this.eventBus.on('data:changed', this.handleDataChange.bind(this));
        this.eventBus.on('data:error', this.handleDataError.bind(this));
        
        // Listen for user actions
        document.addEventListener('click', this.handleGlobalClick.bind(this));
    }
    
    async loadInitialData() {
        const data = await this.asyncController.execute(
            'loadInitialData',
            () => this.apiClient.get('/records'),
            { priority: 'high' }
        );
        
        this.processData(data);
    }
    
    setupRealTimeUpdates() {
        // Set up Server-Sent Events for real-time updates
        const eventSource = new EventSource(`${this.config.apiEndpoint}/events`);
        
        eventSource.onmessage = (event) => {
            const data = JSON.parse(event.data);
            this.handleRealTimeUpdate(data);
        };
        
        eventSource.onerror = (error) => {
            console.error('Real-time connection error:', error);
            this.scheduleReconnection();
        };
    }
    
    handleRealTimeUpdate(data) {
        switch (data.type) {
            case 'record_created':
                this.handleRecordCreated(data.record);
                break;
            case 'record_updated':
                this.handleRecordUpdated(data.record);
                break;
            case 'record_deleted':
                this.handleRecordDeleted(data.recordId);
                break;
        }
    }
    
    async createRecord(recordData) {
        try {
            const newRecord = await this.asyncController.execute(
                `createRecord_${Date.now()}`,
                () => this.apiClient.post('/records', recordData),
                { priority: 'high' }
            );
            
            this.eventBus.emit('data:created', { record: newRecord });
            return newRecord;
            
        } catch (error) {
            this.eventBus.emit('data:error', { operation: 'create', error });
            throw error;
        }
    }
    
    async updateRecord(recordId, updates) {
        try {
            const updatedRecord = await this.asyncController.execute(
                `updateRecord_${recordId}`,
                () => this.apiClient.patch(`/records/${recordId}`, updates),
                { 
                    priority: 'high',
                    cancelPrevious: true // Cancel any pending updates for this record
                }
            );
            
            this.eventBus.emit('data:updated', { record: updatedRecord });
            return updatedRecord;
            
        } catch (error) {
            this.eventBus.emit('data:error', { operation: 'update', error });
            throw error;
        }
    }
    
    async deleteRecord(recordId) {
        try {
            await this.asyncController.execute(
                `deleteRecord_${recordId}`,
                () => this.apiClient.delete(`/records/${recordId}`),
                { priority: 'high' }
            );
            
            this.eventBus.emit('data:deleted', { recordId });
            
        } catch (error) {
            this.eventBus.emit('data:error', { operation: 'delete', error });
            throw error;
        }
    }
    
    async batchOperation(operations) {
        try {
            const results = await this.asyncController.execute(
                'batchOperation',
                () => this.apiClient.post('/batch', { operations }),
                { priority: 'normal', timeout: 60000 }
            );
            
            this.eventBus.emit('data:batchCompleted', { results });
            return results;
            
        } catch (error) {
            this.eventBus.emit('data:error', { operation: 'batch', error });
            throw error;
        }
    }
}

// Initialize when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
    window.dataManager = new PowerPagesDataManager({
        apiEndpoint: '/api/v2',
        enableRealTime: true,
        pageSize: 25,
        tableColumns: [
            { field: 'name', title: 'Name', sortable: true },
            { field: 'email', title: 'Email', sortable: true },
            { field: 'status', title: 'Status', filterable: true },
            { field: 'createdAt', title: 'Created', sortable: true }
        ]
    });
});

Troubleshooting Common Issues

Advanced JavaScript Debugging:

  1. Memory Leaks: Use proper cleanup patterns and WeakMap/WeakSet for object references
  2. Async Race Conditions: Implement proper cancellation and operation queuing
  3. Event Listener Cleanup: Always remove event listeners in component destruction
  4. Module Dependency Issues: Use proper import/export patterns and avoid circular dependencies
  5. Performance Bottlenecks: Profile code execution and optimize critical paths

JavaScript Best Practices

Enterprise-Grade Development Standards:

  1. Type Safety: Use JSDoc comments or TypeScript for better type checking
  2. Error Boundaries: Implement comprehensive error handling at component and application levels
  3. Testing Strategy: Write unit tests for utility functions and integration tests for components
  4. Code Organization: Use consistent file structure and naming conventions
  5. Documentation: Maintain clear documentation for all public APIs and complex logic

Key Takeaways

Advanced JavaScript development enables sophisticated Power Pages functionality through modern patterns, asynchronous programming, and component-based architecture. Focus on maintainable code organization, robust error handling, and performance optimization to build enterprise-grade portal solutions that scale effectively and provide exceptional user experiences.

In the next episode, you’ll learn about Custom API Development to build powerful backend integrations that complement your advanced JavaScript implementations.

3. Quality Assurance

Advanced solutions require rigorous testing:

Practical Implementation

When implementing advanced Power Pages features:

  1. Start with Clear Requirements: Document exactly what functionality is needed
  2. Design for Scale: Consider how the solution will perform as usage grows
  3. Implement Security by Design: Build security considerations into every component
  4. Plan for Maintenance: Create solutions that can be updated and maintained over time

Enterprise Architecture Patterns

Pattern Use Case Benefits Complexity
API Gateway External integrations Centralized security, rate limiting High
Event-Driven Real-time updates Scalability, loose coupling Medium
Microservices Complex business logic Independent deployment, technology diversity High
CQRS Read/write optimization Performance, scalability High

Advanced Troubleshooting

Advanced implementations require sophisticated troubleshooting approaches:

Production Considerations

Deployment Best Practices

Monitoring and Maintenance

Summary

Advanced Power Pages development enables enterprise-scale solutions that integrate seamlessly with complex business requirements. Focus on architecture, follow enterprise development practices, and always plan for long-term maintenance and scalability. These advanced techniques transform Power Pages from a simple portal platform into a robust enterprise application platform.

In the next episode, you’ll continue building on these advanced concepts to create even more sophisticated portal solutions.