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)
200: The Proxy and Reflect APIs
Imagine you're a high-profile CEO. You're busy, and you don't want every single person in the company knocking on your office door to ask for things. Instead, you hire a personal assistant. This assistant sits outside your door. When someone comes to ask you a question or give you a document, they have to go through the assistant first.
The assistant can do a few things: they can block people they don't like, they can log every single request in a notebook, or they can even rewrite the request before it ever reaches your desk. If the assistant decides everything is fine, they simply pass the request through to you. You, the CEO, are the "target," and the assistant is the "Proxy."
Intercepting the Flow with Traps
In JavaScript, a Proxy object allows you to wrap another object and intercept fundamental operations—like looking up a property or changing a value. We call these interception points "traps."
I often use this when I need to add validation to an object without cluttering the object's own logic. Let's say we have a userSettings object, and we want to make sure the volume is never set above 100 or below 0. Instead of writing a setter method for every single property, we can just wrap the whole object in a Proxy.
const userSettings = {
volume: 50,
theme: 'dark'
};
const settingsHandler = {
set(target, prop, value) {
if (prop === 'volume') {
if (value < 0 || value > 100) {
console.error("Volume must be between 0 and 100!");
return false; // Indicates the assignment failed
}
}
target[prop] = value;
return true; // Indicates success
}
};
const proxySettings = new Proxy(userSettings, settingsHandler);
proxySettings.volume = 75; // Works fine
proxySettings.volume = 150; // Logs error, value remains 75
Why we lean on Reflect
You might look at the code above and think, "Why not just use target[prop] = value? Why do I need the Reflect API?"
Here is the thing: manually manipulating the target can get messy, especially when you're dealing with inherited properties or complex objects. Reflect is a built-in object that provides methods for the same operations the Proxy intercepts. It's essentially the "standard way" to perform the default action of a trap.
When I write Proxies in production, I almost always use Reflect. It ensures that the internal behavior of the engine (like returning the correct boolean for success) is handled exactly as the language intended. It makes the Proxy "transparent."
const settingsHandler = {
set(target, prop, value) {
if (prop === 'volume' && (value < 0 || value > 100)) {
throw new Error("Invalid volume level");
}
// Instead of target[prop] = value, we use Reflect
return Reflect.set(target, prop, value);
},
get(target, prop) {
console.log(`Property ${prop} was accessed`);
return Reflect.get(target, prop);
}
};
Creating a "Virtual" Property Layer
One of the coolest things you can do with Proxies is handle properties that don't actually exist on the target object. This is great for creating flexible APIs or mocks for testing.
I've used this to build "smart" configuration objects that return a default value if a specific key is missing, rather than just returning undefined. It keeps the rest of the codebase clean because you don't have to write settings.theme || 'light' everywhere.
const defaults = { theme: 'light', language: 'en' };
const userPrefs = { theme: 'dark' };
const prefHandler = {
get(target, prop) {
return Reflect.get(target, prop) ?? Reflect.get(defaults, prop);
}
};
const finalPrefs = new Proxy(userPrefs, prefHandler);
console.log(finalPrefs.theme); // 'dark' (from userPrefs)
console.log(finalPrefs.language); // 'en' (fallback to defaults)
📋 Practical Task
Exercise: Building a Read-Only State Guard
In many state-management patterns, you want to prevent certain parts of your application state from being modified directly to avoid bugs. Your task is to create a "State Guard."
Requirements:
- Create a target object called
appStatewith the following properties:user: "Alice",isLoggedIn: true, andapiToken: "secret-123". - Create a Proxy that wraps
appState. - Implement a
settrap that checks if the property being changed is"apiToken". - If someone tries to change the
apiToken, the Proxy should throw an Error saying:"The apiToken is read-only!". - For any other property (like
user), the Proxy should allow the update usingReflect.set. - Test your Proxy by trying to update the
user(should work) and then trying to update theapiToken(should throw the error).
There are no comments for now.