James Williams
Birmingham Newman University
jwilliams@staff.newman.ac.uk
3-hour session
// 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'); })();
// 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);
Closures allow functions to access variables from their outer scope even after the outer function has returned.
Explore advanced JavaScript concepts with interactive examples!
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;
}
};
}
// 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}`;
}
}
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"
extends for inheritancesuper() in constructor// 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);
}
// 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));
// 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();
};
// 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');
// 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
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
// 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;
}
}
// 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);
};
}
// 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);
});
});
// 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;
}
advanced-library.js
Time: 45 minutes
This task will help you understand advanced JavaScript concepts and patterns
Take a break, ask questions, or catch up on the previous task.
Next: Secondary slides and Task 2
// 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 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);
});
}
}
// 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);
};
// 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);
}
Time: 45 minutes
This task will help you understand full-stack JavaScript development
// 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));
Web APIs and External Libraries - Working with third-party services