Mastering Advanced JavaScript: 10 Challenging Exercises
JavaScript Developer WorldWide
Join the JavaScript Developers worldwide JavaScript Developers JavaScript Coders JavaScript Freelancers
JavaScript is a versatile and powerful programming language that underpins modern web development. As you advance in your JavaScript journey, tackling more complex and sophisticated exercises can significantly enhance your skills. Here are 10 advanced JavaScript coding exercises designed to challenge your understanding and help you master the language. Each exercise comes with complete code and explanations to ensure a comprehensive learning experience.
Exercise 1: Implementing a Debounce Function
Objective: Create a debounce function to limit the rate at which a function can fire.
Code:
function debounce(func, delay) {
let debounceTimer;
return function(...args) {
const context = this;
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => func.apply(context, args), delay);
}
}
// Example usage
const logMessage = debounce(() => console.log('Debounced function executed'), 2000);
logMessage();
logMessage();
logMessage(); // Only the last call will execute after 2 seconds
Explanation: The debounce function prevents a function from being called repeatedly within a short period. It uses setTimeout to delay the execution and clearTimeout to reset the timer if the function is called again within the delay period.
Exercise 2: Implementing a Throttle Function
Objective: Create a throttle function to limit the rate at which a function can fire.
Code:
function throttle(func, limit) {
let lastFunc;
let lastRan;
return function(...args) {
const context = this;
if (!lastRan) {
func.apply(context, args);
lastRan = Date.now();
} else {
clearTimeout(lastFunc);
lastFunc = setTimeout(() => {
if ((Date.now() - lastRan) >= limit) {
func.apply(context, args);
lastRan = Date.now();
}
}, limit - (Date.now() - lastRan));
}
}
}
// Example usage
const logMessage = throttle(() => console.log('Throttled function executed'), 2000);
logMessage();
logMessage();
logMessage(); // Only the first call will execute immediately, the rest will execute after the specified limit
Explanation: The throttle function limits the execution of a function to once every specified period. It uses setTimeout to schedule the next execution if the function is called again before the limit period has passed.
Exercise 3: Deep Cloning an Object
Objective: Implement a function to deeply clone a JavaScript object.
Code:
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (obj instanceof Array) {
let copy = [];
for (let i = 0; i < obj.length; i++) {
copy[i] = deepClone(obj[i]);
}
return copy;
}
if (obj instanceof Object) {
let copy = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = deepClone(obj[key]);
}
}
return copy;
}
}
// Example usage
const original = { a: 1, b: { c: 2, d: [3, 4] } };
const cloned = deepClone(original);
console.log(cloned); // Output: { a: 1, b: { c: 2, d: [3, 4] } }
Explanation: The deepClone function recursively clones all properties of an object, ensuring that nested objects and arrays are also cloned.
Exercise 4: Implementing a Simple Pub/Sub System
Objective: Create a simple publish-subscribe system.
Code:
class PubSub {
constructor() {
this.events = {};
}
subscribe(event, listener) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(listener);
}
unsubscribe(event, listenerToRemove) {
if (!this.events[event]) return;
this.events[event] = this.events[event].filter(listener => listener !== listenerToRemove);
}
publish(event, data) {
if (!this.events[event]) return;
this.events[event].forEach(listener => listener(data));
}
}
// Example usage
const pubsub = new PubSub();
const onMessage = data => console.log('Message received:', data);
pubsub.subscribe('message', onMessage);
pubsub.publish('message', { text: 'Hello, world!' }); // Output: Message received: { text: 'Hello, world!' }
pubsub.unsubscribe('message', onMessage);
pubsub.publish('message', { text: 'Hello again!' }); // No output
Explanation: The PubSub class manages event listeners for different events, allowing for the publishing of data to all subscribed listeners.
Exercise 5: Implementing a Simple Promise-based Retry Function
Objective: Create a function that retries a promise-returning function a specified number of times.
Code:
function retry(fn, retries = 3, delay = 1000) {
return new Promise((resolve, reject) => {
const attempt = () => {
fn()
.then(resolve)
.catch((error) => {
if (retries === 0) {
reject(error);
} else {
retries--;
setTimeout(attempt, delay);
}
});
};
attempt();
});
}
// Example usage
const fetchData = () => fetch('https://api.example.com/data').then(response => response.json());
retry(fetchData, 3, 2000)
.then(data => console.log('Data received:', data))
.catch(error => console.error('Failed after 3 retries:', error));
Explanation: The retry function retries a promise-returning function a specified number of times, with a delay between each attempt.
领英推荐
Exercise 6: Creating a Simple LRU Cache
Objective: Implement a simple Least Recently Used (LRU) cache.
Code:
class LRUCache {
constructor(limit) {
this.limit = limit;
this.cache = new Map();
}
get(key) {
if (!this.cache.has(key)) {
return null;
}
const value = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
set(key, value) {
if (this.cache.has(key)) {
this.cache.delete(key);
} else if (this.cache.size >= this.limit) {
const oldestKey = this.cache.keys().next().value;
this.cache.delete(oldestKey);
}
this.cache.set(key, value);
}
}
// Example usage
const cache = new LRUCache(2);
cache.set('a', 1);
cache.set('b', 2);
console.log(cache.get('a')); // Output: 1
cache.set('c', 3);
console.log(cache.get('b')); // Output: null (removed because of the limit)
console.log(cache.get('c')); // Output: 3
Explanation: The LRUCache class maintains a cache with a specified limit, removing the least recently used items when the limit is exceeded.
Exercise 7: Implementing a Custom Event Emitter
Objective: Create a custom event emitter class.
Code:
class EventEmitter {
constructor() {
this.events = {};
}
on(event, listener) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(listener);
}
off(event, listenerToRemove) {
if (!this.events[event]) return;
this.events[event] = this.events[event].filter(listener => listener !== listenerToRemove);
}
emit(event, data) {
if (!this.events[event]) return;
this.events[event].forEach(listener => listener(data));
}
}
// Example usage
const emitter = new EventEmitter();
const onEvent = data => console.log('Event received:', data);
emitter.on('event', onEvent);
emitter.emit('event', { message: 'Hello, world!' }); // Output: Event received: { message: 'Hello, world!' }
emitter.off('event', onEvent);
emitter.emit('event', { message: 'Hello again!' }); // No output
Explanation: The EventEmitter class allows for registering, deregistering, and emitting events with data.
Exercise 8: Implementing a Simple Router
Objective: Create a simple client-side router.
Code:
class Router {
constructor() {
this.routes = {};
window.addEventListener('hashchange', this.hashChangeHandler.bind(this));
window.addEventListener('load', this.hashChangeHandler.bind(this));
}
register(route, callback) {
this.routes[route] = callback;
}
hashChangeHandler() {
const hash = window.location.hash.substring(1);
if (this.routes[hash]) {
this.routes[hash]();
}
}
}
// Example usage
const router = new Router();
router.register('home', () => console.log('Home route'));
router.register('about', () => console.log('About route'));
// Navigate to #home or #about in the URL to see the respective messages.
Explanation: The Router class listens for hash changes and executes the corresponding callback function based on the registered routes.
Exercise 9: Creating a Simple Middleware System
Objective: Implement a middleware system similar to Express.js.
Code:
class Middleware {
constructor() {
this.middlewares = [];
}
use(fn) {
this.middlewares.push(fn);
}
execute(context) {
const dispatch = (i) => {
if (i >= this.middlewares.length) return;
const middleware = this.middlewares[i];
middleware(context, () => dispatch(i + 1));
};
dispatch(0);
}
}
// Example usage
const app = new Middleware();
app.use((ctx, next) => {
console.log('Middleware 1');
next();
});
app.use((ctx, next) => {
console.log('Middleware 2');
next();
});
app.execute({});
// Output:
// Middleware 1
// Middleware 2
Explanation: The Middleware class manages an array of middleware functions and executes them in sequence.
Exercise 10: Implementing a Custom Iterable
Objective: Create a custom iterable object.
Code:
class CustomIterable {
constructor(data) {
this.data = data;
}
[Symbol.iterator]() {
let index = 0;
let data = this.data;
return {
next() {
if (index < data.length) {
return { value: data[index++], done: false };
} else {
return { done: true };
}
}
};
}
}
// Example usage
const iterable = new CustomIterable([1, 2, 3, 4]);
for (const value of iterable) {
console.log(value);
}
// Output:
// 1
// 2
// 3
// 4
Explanation: The CustomIterable class implements the iterator protocol by defining a [Symbol.iterator] method that returns an iterator object with a next method.
These advanced JavaScript exercises cover a range of topics, from implementing debounce and throttle functions to creating custom iterables and middleware systems. They provide a deeper understanding of JavaScript concepts and enhance coding skills
?? Self Taught Web Developer Student ? HTML ? JavaScript ? CSS ? React JS ? Node.js ?
7 个月Good point!