Elevating JavaScript Skills 10 Advanced Exercises for Modern Developers

Elevating JavaScript Skills: 10 Advanced Exercises for Modern Developers

In an ever-evolving digital world, staying ahead in web development means constantly honing your skills and embracing the latest JavaScript features. This blog post delves into ten advanced exercises designed to elevate your JavaScript knowledge and coding prowess. From dynamic property management to code splitting and beyond, these exercises will challenge you to explore cutting-edge features and coding patterns.

Exercise 11: Using Reflect API for Dynamic Property Management

Dive into the Reflect API, a modern approach to object operations. This exercise guides you through dynamic property management, offering a more flexible and safer way to handle objects compared to traditional methods.

Exercise 12: Symbol Properties for Privacy

Symbols introduce a new way to add private properties to objects, helping you manage object state securely. Learn how to utilize Symbol properties to encapsulate data and protect it from unintended external access.

Exercise 13: Observer Pattern Implementation

The Observer pattern is pivotal for creating loosely coupled, scalable applications. This exercise will teach you how to implement this pattern, enhancing your ability to design systems that react dynamically to events or data changes.

Exercise 14: Basic Web Worker Usage

Web Workers can significantly improve web application performance by running scripts in background threads. Through this exercise, you’ll learn how to offload tasks, ensuring smoother UIs and enhancing user experience.

Exercise 15: Dynamic Imports for Code Splitting

Dynamic imports are a game-changer for optimizing your application’s load time. This exercise introduces you to code splitting, allowing you to load resources on demand and reduce initial load times.

Exercise 16: Working with Maps

The Map object provides a robust structure for storing key-value pairs with any value type. Explore how to utilize Maps for efficient data storage and retrieval, surpassing the limitations of traditional objects.

Exercise 17: Utilizing Sets for Uniqueness

Sets are a powerful feature for storing unique values. Learn how to leverage Sets to manage data more effectively, ensuring that each element is distinct and optimizing data handling.

Exercise 18: Template Literals and Tagged Templates

Template literals offer enhanced syntax for embedding expressions into strings. This exercise takes you further into tagged templates, enabling more sophisticated string construction and manipulation techniques.

Exercise 19: ES6 Classes with Inheritance

ES6 classes streamline object-oriented programming in JavaScript. Master class syntax, inheritance, and polymorphism to construct well-organized, reusable code structures.

Exercise 20: Deep Cloning with Spread Operator

While the spread operator is commonly used for shallow cloning, this exercise challenges you to achieve deep cloning. Unlock new strategies to duplicate complex data structures without retaining references to the original objects.

Exercise 11: Using Reflect API for Dynamic Property Management

Objective: Create a dynamic object manager that uses the Reflect API to safely set and get properties on objects, including logging for property access and changes.

const objectManager = {

 set(target, property, value) {

 console.log(`Setting value ‘${value}’ to property ‘${property}’`);

 return Reflect.set(target, property, value);

 },

 get(target, property) {

 console.log(`Getting property ‘${property}’`);

 return Reflect.get(target, property);

 }

};

const myObject = new Proxy({}, objectManager);

myObject.name = ‘John’; // Logs: Setting value ‘John’ to property ‘name’

console.log(myObject.name); // Logs: Getting property ‘name’ then ‘John’

Exercise 12: Symbol Properties for Privacy

Objective: Implement a module that uses Symbol properties to store private data for an object, providing public methods to interact with that data.

const PersonModule = (() => {

 const nameSymbol = Symbol(‘name’);

 class Person {

 constructor(name) {

 this[nameSymbol] = name;

 }

 getName() {

 return this[nameSymbol];

 }

 setName(name) {

 this[nameSymbol] = name;

 }

 }

 return Person;

})();

const person = new PersonModule(‘Alice’);

console.log(person.getName()); // Alice

person.setName(‘Bob’);

console.log(person.getName()); // Bob

Exercise 13: Observer Pattern Implementation

Objective: Implement the Observer pattern to create a simple subscription model, allowing objects to “subscribe” to events and be notified when they occur.

class Observable {

 constructor() {

 this.observers = [];

 }

 subscribe(observer) {

 this.observers.push(observer);

 }

 unsubscribe(observer) {

 this.observers = this.observers.filter(subscriber => subscriber !== observer);

 }

 notify(data) {

 this.observers.forEach(observer => observer(data));

 }

}

const observable = new Observable();

const logger = data => console.log(`Received data: ${data}`);

observable.subscribe(logger);

observable.notify(‘Hello World!’); // Logs: Received data: Hello World!

Exercise 14: Basic Web Worker Usage

Objective: Create a simple Web Worker that performs a long-running calculation (e.g., sum of numbers) and sends the result back to the main script.

Note: This exercise requires you to write two separate scripts. One for the main page and another for the worker.

  • Main Script (main.js):

if (window.Worker) {

 const myWorker = new Worker(‘worker.js’);

 myWorker.postMessage([1, 10000000]); // Example range

 myWorker.onmessage = function(e) {

 console.log(`Sum: ${e.data}`);

 };

}

  • Web Worker Script (worker.js):

self.onmessage = function(e) {

 const [start, end] = e.data;

 let sum = 0;

 for (let i = start; i <= end; i++) {

 sum += i;

 }

 self.postMessage(sum);

};

Exercise 15: Dynamic Imports for Code Splitting

Objective: Dynamically import a module when it’s needed, rather than loading it at the start. Use this to load a math utility module only when a certain condition is met.

async function loadMathModuleIfNeeded(condition) {

 if (condition) {

 const mathModule = await import(‘./mathUtils.js’);

 console.log(‘Module loaded:’, mathModule);

 return mathModule;

 } else {

 console.log(‘No need to load the math module.’);

 return null;

 }

}

loadMathModuleIfNeeded(true) // Conditionally loads `mathUtils.js`

 .then(module => {

 if (module) {

 console.log(module.add(2, 3)); // Assuming `add` is a function exported by mathUtils.js

 }

 });

Exercise 16: Working with Maps

Objective: Create a function that takes an array of strings. It should return a Map where each key is one of the strings and its value is the number of times the string appears in the array.

function countStringOccurrences(array) {

 const map = new Map();

 array.forEach(item => {

 const count = map.get(item) || 0;

 map.set(item, count + 1);

 });

 return map;

}

const fruits = [“apple”, “banana”, “apple”, “orange”, “banana”, “apple”];

console.log(countStringOccurrences(fruits));

// Expected output: Map(3) { ‘apple’ => 3, ‘banana’ => 2, ‘orange’ => 1 }

Exercise 17: Utilizing Sets for Uniqueness

Objective: Implement a function that takes two arrays and returns an array of the unique elements present in both arrays, without using any loop or the filter method.

function findUniqueElements(arr1, arr2) {

 const set1 = new Set(arr1);

 const set2 = new Set(arr2);

 const intersection = new Set([…set1].filter(x => set2.has(x)));

 return Array.from(intersection);

}

const array1 = [1, 2, 3, 4, 5];

const array2 = [4, 5, 6, 7, 8];

console.log(findUniqueElements(array1, array2)); // [4, 5]

Exercise 18: Template Literals and Tagged Templates

Objective: Write a tagged template function highlight that wraps the interpolated expressions in a string with <mark> tags. The function should return the resulting HTML string.

function highlight(strings, …values) {

 return strings.reduce((result, string, i) => {

 return `${result}${string}<mark>${values[i] || ”}</mark>`;

 }, ”);

}

const name = “John”;

const domain = “example.com”;

console.log(highlight`Welcome, ${name}! Your domain is ${domain}.`);

// Expected output: “Welcome, <mark>John</mark>! Your domain is <mark>example.com</mark>.”

Exercise 19: ES6 Classes with Inheritance

Objective: Create an ES6 class Vehicle, and a subclass Car that extends Vehicle. The Vehicle class should have a method start that returns “Vehicle started”, and the Car class should override this method to return “Car started”. Include a constructor in the Car class that accepts a model parameter.

class Vehicle {

 start() {

 return “Vehicle started”;

 }

}

class Car extends Vehicle {

 constructor(model) {

 super();

 this.model = model;

 }

 start() {

 return `Car started. Model: ${this.model}`;

 }

}

const myCar = new Car(“Toyota”);

console.log(myCar.start()); // “Car started. Model: Toyota”

Exercise 20: Deep Cloning with Spread Operator

Objective: Write a function that deep clones an object or array. This function should handle nested objects and arrays. Note that the spread operator … provides shallow cloning, so you’ll need to enhance its usage for deep cloning.

function deepClone(item) {

 if (Array.isArray(item)) {

 return item.map(i => deepClone(i));

 } else if (typeof item === ‘object’ && item !== null) {

 const clone = {};

 for (const key in item) {

 clone[key] = deepClone(item[key]);

 }

 return clone;

 } else {

 return item;

 }

}

const original = { a: 1, b: { c: 2, d: [3, 4, { e: 5 }] } };

const cloned = deepClone(original);

console.log(cloned); // { a: 1, b: { c: 2, d: [3, 4, { e: 5 }] } }