Skip to Content
Course content

200: The Proxy and Reflect APIs

Click on the "Edit" button in the top corner of the screen to edit your slide content.

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 appState with the following properties: user: "Alice", isLoggedIn: true, and apiToken: "secret-123".
  • Create a Proxy that wraps appState.
  • Implement a set trap 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 using Reflect.set.
  • Test your Proxy by trying to update the user (should work) and then trying to update the apiToken (should throw the error).
Rating
0 0

There are no comments for now.

to be the first to leave a comment.