H M < >
CMU422: Web Design - Session 8

JavaScript Advanced Concepts

Professional Web Design and Development

James Williams

Birmingham Newman University

jwilliams@staff.newman.ac.uk

3-hour session

Learning Objectives

  • Master advanced function concepts
  • Understand object-oriented programming
  • Learn modern JavaScript features
  • Work with asynchronous programming
  • Apply functional programming concepts

Advanced Function Concepts

Functions are first-class citizens in JavaScript
// Function declaration
function greet(name) { return `Hello ${name}`; }
// Function expression
const greet = function(name) { return `Hello ${name}`; };
// Arrow function
const greet = (name) => `Hello ${name}`;
// Immediately Invoked Function Expression (IIFE)
(function() { console.log('IIFE'); })();
  • Multiple ways to define functions
  • Functions can be assigned to variables
  • Functions can be passed as arguments
  • Functions can return other functions

Higher-Order Functions

// Function that returns a function
function multiply(x) {
return function(y) {
return x * y;
};
}
const multiplyByTwo = multiply(2);
console.log(multiplyByTwo(5)); // 10
// Function that takes a function as argument
function processArray(arr, callback) {
return arr.map(callback);
}
const numbers = [1, 2, 3, 4];
const doubled = processArray(numbers, x => x * 2);
  • Functions that return functions
  • Functions that take functions as arguments
  • Enable functional programming patterns
  • Create reusable and composable code

Try It: Advanced JavaScript Concepts

Closure Demo

Click buttons to see closures in action!

Current Concept:

Closures allow functions to access variables from their outer scope even after the outer function has returned.

Explore advanced JavaScript concepts with interactive examples!

Closures

function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
// Private variables
function createWallet(initialBalance) {
let balance = initialBalance;
return {
getBalance: () => balance,
deposit: (amount) => { balance += amount; },
withdraw: (amount) => {
if (amount <= balance) {
balance -= amount;
return true;
}
return false;
}
};
}
  • Functions that remember their lexical scope
  • Access to variables from outer scope
  • Used for data privacy and state management
  • Common in modern JavaScript patterns

Object-Oriented Programming

// Constructor function
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.greet = function() {
return `Hello, I'm ${this.name}`;
};
const person = new Person('John', 30);
console.log(person.greet()); // "Hello, I'm John"
// ES6 Classes
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
return `Hello, I'm ${this.name}`;
}
}
  • Constructor functions and prototypes
  • ES6 class syntax (syntactic sugar)
  • Inheritance and polymorphism
  • Encapsulation and abstraction

Inheritance

class Animal {
constructor(name) {
this.name = name;
}
speak() {
return `${this.name} makes a sound`;
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
speak() {
return `${this.name} barks`;
}
fetch() {
return `${this.name} fetches the ball`;
}
}
const dog = new Dog('Buddy', 'Golden Retriever');
console.log(dog.speak()); // "Buddy barks"
  • Use extends for inheritance
  • Call super() in constructor
  • Override methods in child classes
  • Add new methods to child classes

Modern JavaScript Features

// Destructuring
const { name, age } = person;
const [first, second, ...rest] = array;
// Spread operator
const newArray = [...oldArray, newItem];
const newObject = { ...oldObject, newProperty: value };
// Template literals
const message = `Hello ${name}, you are ${age} years old`;
// Default parameters
function greet(name = 'Guest') {
return `Hello ${name}`;
}
// Rest parameters
function sum(...numbers) {
return numbers.reduce((a, b) => a + b, 0);
}
  • Destructuring for cleaner code
  • Spread operator for copying and merging
  • Template literals for string interpolation
  • Default and rest parameters

Promises

// Creating a promise
const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const random = Math.random();
    if (random > 0.5) {
      resolve('Success!');
    } else {
    reject('Failed!');
  }
}, 1000);
});
// Using promises
myPromise
.then(result => console.log(result))
.catch(error => console.error(error));
// Promise chaining
fetch('/api/data')
.then(response => response.json())
.then(data => processData(data))
.catch(error => handleError(error));
  • Handle asynchronous operations
  • Better than callbacks
  • Chain multiple operations
  • Error handling with catch

Async/Await

// Async function
async function fetchUserData(userId) {
try {
const response = await
fetch(`/api/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 async function
async function displayUser(userId) {
const user = await fetchUserData(userId);
console.log(`User: ${user.name}`);
}
// Arrow function with async
const loadData = async () => {
const data = await fetch('/api/data');
return data.json();
};
  • Syntactic sugar over promises
  • Makes async code look synchronous
  • Better error handling with try/catch
  • Easier to read and understand

ES6 Modules

// math.js (exporting)
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
export default class Calculator {
  multiply(a, b) { return a * b; }
}
// app.js (importing)
import Calculator, { add, subtract } from './math.js';
const calc = new Calculator();
console.log(add(5, 3)); // 8
console.log(calc.multiply(4, 2)); // 8
// Dynamic imports
const module = await import('./dynamic-module.js');
  • Organize code into separate files
  • Export and import functionality
  • Default and named exports
  • Dynamic imports for code splitting

Functional Programming Concepts

// Pure functions
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
// Function composition
const compose = (...fns) => x =>
fns.reduceRight((acc, fn) => fn(acc), x);
const addOne = x => x + 1;
const double = x => x * 2;
const addOneAndDouble = compose(double, addOne);
console.log(addOneAndDouble(5)); // 12
// Immutability
const original = [1, 2, 3];
const doubled = original.map(x => x * 2);
// original is unchanged
  • Pure functions (no side effects)
  • Function composition
  • Immutability
  • Higher-order functions

Modern Array Methods

const numbers = [1, 2, 3, 4, 5];
// Map
const doubled = numbers.map(x => x * 2);
// [2, 4, 6, 8, 10]
// Filter
const evens = numbers.filter(x => x % 2 === 0);
// [2, 4]
// Reduce
const sum = numbers.reduce((acc, x) => acc + x, 0);
// 15
// Find
const firstEven = numbers.find(x => x % 2 === 0);
// 2
// Some
const hasEven = numbers.some(x => x % 2 === 0);
// true
// Every
const allPositive = numbers.every(x => x > 0);
// true
  • Functional programming approach
  • Chain multiple operations
  • More readable than loops
  • Immutable operations

Advanced Error Handling

// Custom error classes
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = 'ValidationError';
this.field = field;
}
}
// Error handling with async/await
async function processData(data) {
try {
if (!data) {
throw new ValidationError('Data is
required', 'data');
}
const result = await validateAndProcess(data);
return result;
} catch (error) {
if (error instanceof ValidationError) {
console.error(`Validation error in
${error.field}:`, error.message);
} else {
console.error('Unexpected error:',
error);
}
throw error;
}
}
  • Custom error classes
  • Error inheritance
  • Type checking with instanceof
  • Proper error propagation

Performance Optimization

  • Memoization for expensive calculations
  • Debouncing and throttling
  • Lazy loading and code splitting
  • Efficient DOM manipulation
  • Memory leak prevention
// Memoization
function memoize(fn) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
// Debouncing
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}

Testing JavaScript Code

  • Unit testing with Jest or Mocha
  • Test-driven development (TDD)
  • Mocking and stubbing
  • Integration testing
  • Code coverage
// Example test with Jest
function add(a, b) {
return a + b;
}
// test.js
describe('add function', () => {
test('adds two positive numbers', () => {
expect(add(2, 3)).toBe(5);
});
test('adds negative numbers', () => {
expect(add(-1, -2)).toBe(-3);
});
test('handles zero', () => {
expect(add(0, 5)).toBe(5);
});
});

Advanced Debugging

  • Browser developer tools
  • Console methods (log, warn, error, table)
  • Breakpoints and step-through
  • Performance profiling
  • Memory leak detection
// Debugging techniques
console.log('Debug info:', { user, data });
console.table(arrayOfObjects);
console.trace('Function call stack');
// Performance measurement
console.time('operation');
// ... expensive operation
console.timeEnd('operation');
// Debugger statement
function complexFunction() {
let result = 0;
debugger; // Execution stops here
return result;
}

Task 1: Advanced JavaScript Library

Instructions:

  1. Create a new JavaScript file called advanced-library.js
  2. Build a utility library with:
    • Math utilities: Advanced mathematical functions
    • Array utilities: Custom array manipulation methods
    • Object utilities: Deep cloning, merging, validation
    • Async utilities: Promise helpers and async operations
  3. Use modern JavaScript features:
    • ES6 classes and modules
    • Arrow functions and destructuring
    • Promises and async/await
    • Higher-order functions
  4. Implement proper error handling
  5. Add JSDoc documentation
  6. Create unit tests for your functions

Time: 45 minutes

This task will help you understand advanced JavaScript concepts and patterns

Break Time

15 Minutes

Take a break, ask questions, or catch up on the previous task.

Next: Secondary slides and Task 2

Common Design Patterns

  • Singleton: Single instance pattern
  • Factory: Object creation pattern
  • Observer: Event handling pattern
  • Module: Encapsulation pattern
  • Decorator: Function enhancement pattern
// Singleton pattern
class Database {
  constructor() {
    if (Database.instance) {
      return Database.instance;
    }
    Database.instance = this;
  }
}
// Factory pattern
class UserFactory {
  createUser(type, data) {
    switch(type) {
      case 'admin': return new
      AdminUser(data);
      case 'regular': return new
      RegularUser(data);
    }
  }
}

Memory Management

  • Garbage collection
  • Memory leaks prevention
  • Event listener cleanup
  • Closure memory considerations
  • WeakMap and WeakSet usage
// Memory leak prevention
class EventManager {
  constructor() {
    this.listeners = new Map();
  }
  addListener(element, event, handler) {
    element.addEventListener(event, handler);
    this.listeners.set(element, { event, handler });
  }
  removeListener(element) {
    const listener = this.listeners.get(element);
    if (listener) {
      element.removeEventListener(listener.event,
      listener.handler);
      this.listeners.delete(element);
    }
  }
  cleanup() {
    this.listeners.forEach((listener, element) => {
      this.removeListener(element);
    });
  }
}

Security Best Practices

  • Input validation and sanitization
  • XSS prevention
  • CSRF protection
  • Secure coding practices
  • Content Security Policy (CSP)
// Input validation
function validateInput(input) {
if (typeof input !== 'string') {
throw new Error('Input must be a string');
}
return input.replace(/[<>]/g, ''); // Basic XSS prevention
}
// Secure object property access
const safeGet = (obj, path) => {
return path.split('.').reduce((current, key) => {
return current && current[key] !== undefined ?
current[key] : null;
}, obj);
};

Code Quality and Standards

  • ESLint for code linting
  • Prettier for code formatting
  • JSDoc for documentation
  • TypeScript for type safety
  • Code review practices
// JSDoc documentation
/**
* Calculates the factorial of a number
* @param {number} n - The number to calculate factorial for
* @returns {number} The factorial of n
* @throws {Error} If n is negative
*/
function factorial(n) {
if (n < 0) {
throw new Error('Factorial is not defined for
negative numbers');
}
if (n === 0 || n === 1) {
return 1;
}
return n * factorial(n - 1);
}

Task 2: Complete Web Application

Instructions:

  1. Create a complete web application using all concepts learned
  2. Build a task management app with:
    • Architecture: Modular design with ES6 modules
    • Data Management: Local storage with CRUD operations
    • UI Components: Reusable components using classes
    • Async Operations: Simulated API calls with promises
    • Event Handling: Advanced event delegation and custom events
  3. Implement advanced features:
    • Task filtering and sorting
    • Drag and drop functionality
    • Real-time search with debouncing
    • Data validation and error handling
    • Responsive design with modern CSS
  4. Use design patterns (Factory, Observer, Module)
  5. Add comprehensive error handling and logging
  6. Include unit tests for core functionality

Time: 45 minutes

This task will help you understand full-stack JavaScript development

Common Advanced Issues

  • Scope issues: Check closure and context
  • Async problems: Promise rejection handling
  • Memory leaks: Event listener cleanup
  • Performance issues: Profiling and optimization
  • Module errors: Import/export syntax
  • Type errors: Runtime type checking
// Common debugging patterns
// Check if function is called
console.log('Function called with:', arguments);
// Check promise state
promise.then(result => {
console.log('Promise resolved:', result);
}).catch(error => {
console.error('Promise rejected:', error);
});
// Check object structure
console.log('Object keys:', Object.keys(obj));
console.log('Object values:', Object.values(obj));

Session Summary

  • Master advanced function concepts and closures
  • Understand object-oriented programming
  • Use modern JavaScript features effectively
  • Handle asynchronous operations properly
  • Apply functional programming concepts
  • Write maintainable and testable code

Next Session:

Web APIs and External Libraries - Working with third-party services