JavaScript
Completed
-
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)
14: Tagged Templates
A few years ago, I was reviewing a PR for a junior developer who was building a custom dashboard. He was using standard template literals to inject user-generated content directly into the innerHTML of a page. To prevent XSS attacks, he was manually calling an escapeHTML() function on every single variable. It looked like this: <div>Hello, ${escapeHTML(userName)}</div>. It was tedious, and more importantly, it was fragile. He missed one single variable in a deeply nested component, and during QA, we found that a user could literally rename themselves to a <script> tag and execute code in another user's browser. It was a classic "human error" bug.
This is exactly why Tagged Templates exist. Instead of remembering to call a sanitizer function every time you interpolate a value, you can create a "tag" function that intercepts the template literal and handles the sanitization for you automatically. It moves the responsibility from the developer's memory to the language's syntax.
Parsing the Pieces
Normally, when you use a template literal, JavaScript evaluates the expressions and returns a finished string. But when you prefix that template with a function name—like myTag`hello ${name}`—JavaScript doesn't just concatenate the string. Instead, it calls your function and passes it the template in a very specific way.
The first argument is an array of the static string parts. The subsequent arguments are the evaluated results of the expressions. I like to think of it as the engine "chopping" your string wherever a ${} occurs. If you have two variables, you'll have three static string segments (the part before the first variable, the part between the two, and the part after the second).
function debugTag(strings, ...values) {
console.log('Static strings:', strings);
console.log('Interpolated values:', values);
}
const user = 'Alice';
const action = 'uploaded a file';
debugTag`User ${user} has ${action} to the server.`;
// Output:
// Static strings: ["User ", " has ", " to the server."]
// Interpolated values: ["Alice", "uploaded a file"]
The ...values syntax is crucial here because you never know how many expressions a user will put in their template. By using the rest parameter, you capture all of them in an array.
Applying it to Real-World Security
Now, let's look at how we can solve that XSS problem I mentioned. We can write a tag function that iterates through those strings and values, but runs the values through a sanitizer before stitching them back together. I've found this is the cleanest way to handle dynamic HTML generation without relying on a massive framework for every small project.
function safeHtml(strings, ...values) {
return strings.reduce((result, string, i) => {
let value = values[i] || '';
// Simple escaping logic: convert special chars to HTML entities
const escapedValue = String(value)
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"');
return result + string + escapedValue;
}, '');
}
const untrustedInput = '<img src=x onerror=alert("Hacked!")>';
const output = safeHtml`<div class="user-profile">Welcome, ${untrustedInput}</div>`;
console.log(output);
// Result: <div class="user-profile">Welcome, <img src=x onerror=alert("Hacked!")></div>
Notice how we used reduce here. We start with an empty string and progressively add a static piece followed by its corresponding escaped value. Since there is always one more string piece than there are values, we handle the final string segment on the last iteration where values[i] is undefined. It's an elegant pattern that makes your code significantly more robust.
📋 Practical Task
Build a Localized Translation Tag
In many professional applications, you can't hardcode strings because the app needs to support multiple languages. Your task is to create a tagged template called i18n (short for internationalization) that looks up values in a translation dictionary.
Requirements:
- Create a
translationsobject that contains keys for'greeting'and'farewell'in both English ('en') and Spanish ('es'). - Implement the
i18ntag function. If a value passed into the template is a key found in the translation dictionary for the current active language, replace it with the translated string. If the key isn't found, leave the value as is. - The function should use a global variable
currentLangto determine which language to use.
Example Usage:
let currentLang = 'es';
const userName = 'Carlos';
// Should output: "Hola, Carlos! Adiós!"
console.log(i18n` ${'greeting'}, ${userName}! ${'farewell'}!`);
There are no comments for now.