-
Section 1: Getting Started
-
Section 2: Core Syntax and Types
-
Section 3: Strings and Numbers in Depth
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Objects and Arrays
-
Section 7: Maps, Sets, and Symbols
-
Section 8: Asynchronous JavaScript
-
Section 9: Object-Oriented and Prototypes
-
Section 10: The DOM
-
Section 11: Browser APIs
-
Section 12: Modern JavaScript (ES2015-ES2025)
-
Section 13: Functional Programming Patterns
-
Section 14: Error Handling and Debugging
-
Section 15: Testing
-
Section 16: Accessibility for JavaScript Developers
-
Section 17: Internationalization and Localization
-
Section 18: Performance
-
Section 19: Node.js Fundamentals
-
Section 20: Regular Expressions
-
Section 21: Design Patterns in JavaScript
-
Section 22: Security Basics
-
Section 23: Data Structures and Algorithms in JavaScript
-
Section 24: Practical Projects
-
Section 25: More Advanced Async Patterns
-
Section 26: More Object and Class Practice
-
Section 27: Working with Dates and Internationalization
-
Section 28: Web Components
-
Section 29: More DOM and Browser Practice
-
Section 30: Build Tooling for Vanilla JavaScript
-
Section 31: More Practice Projects
-
Section 32: Interview and Algorithm Practice
-
Section 33: Error Objects (MDN Reference)
-
Section 34: TypedArrays and Binary Data
-
Section 35: Reflection and Metaprogramming (MDN Reference)
-
Section 36: More Global Functions (MDN Reference)
22: Practice Exercise: Building a Text Formatter Utility
Think of a text formatter like a professional copy editor at a magazine. When a writer submits a rough draft, it's often a mess—random capitalization, double spaces where there should be one, and inconsistent formatting. The editor doesn't just rewrite the whole thing from scratch; they apply a set of consistent rules: "Capitalize the headlines," "Remove the trailing spaces," "Convert the date format." They take a raw input and run it through a pipeline of specific transformations until it's polished and ready for print.
In JavaScript, we're going to build that exact pipeline. The "rough draft" is your input string, the "editor's rules" are your string methods (like .trim(), .toLowerCase(), and .replace()), and the "final page" is the returned value from your utility function.
Designing your transformation toolkit
When I build these kinds of utilities in production, I avoid writing one massive, bloated function. That's a nightmare to debug. Instead, I prefer a "toolbox" approach. I create small, single-purpose functions that do one thing perfectly, and then I wrap them in a main controller. It makes your code readable and, more importantly, testable.
const TextToolbox = {
toTitleCase: (str) => {
return str
.toLowerCase()
.split(' ')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
},
slugify: (str) => {
return str
.toLowerCase()
.trim()
.replace(/[^\w\s-]/g, '') // Remove special characters
.replace(/[\s_-]+/g, '-') // Replace spaces and underscores with a single hyphen
.replace(/^-+|-+$/g, ''); // Trim hyphens from ends
}
};
Notice the slugify method. I used a Regular Expression there because trying to manually loop through a string to find every single special character is a waste of your time. Regex is the "power tool" of text formatting.
Connecting the pipes
The real magic happens when you chain these operations together. You don't want to manually call toTitleCase and then pass that result into another function. You want a flow. I usually implement this by creating a format function that takes the text and an array of "rules" to apply.
I've found that this approach keeps the logic decoupled. If you suddenly decide you no longer want to slugify your URLs, you just remove that one rule from the array rather than digging through a 50-line function to find where the logic is buried.
function applyFormatter(text, rules) {
return rules.reduce((processedText, rule) => {
return rule(processedText);
}, text);
}
// Usage:
const rawInput = " HELLO world, this is a TEST! ";
const myRules = [
(s) => s.trim(),
TextToolbox.toTitleCase,
(s) => s + " (Verified)"
];
const result = applyFormatter(rawInput, myRules);
console.log(result); // "Hello World, This Is A Test! (Verified)"
By using .reduce(), we're essentially passing the string through an assembly line. Each function in the myRules array takes the result of the previous one and modifies it further. It's clean, it's scalable, and it's how professional utility libraries are actually structured.
📋 Practical Task
Exercise: The Social Media Caption Polisher
You've been asked to build a utility that cleans up messy user-generated captions before they are posted to a social media feed. The captions are often riddled with erratic casing and accidental whitespace.
Your Task: Create a script that implements the following requirements:
- Create a
CaptionUtilsobject containing two methods:cleanWhitespace: Removes leading/trailing whitespace and replaces any instance of multiple spaces (two or more) with a single space.forceLowercase: Converts the entire string to lowercase.
- Write a function called
polishCaptionthat takes a string and an array of functions (the rules). It should use.reduce()to apply those rules to the string. - Test your code with the following input:
" I LOVE JavaScript SO much!! ". - The final output should be:
"i love javascript so much!! (posted)". (Hint: You'll need to add a custom anonymous function to your rules array to append the " (posted)" suffix).
There are no comments for now.