Diego Betto's Blog
Photo by Jørgen Håland

February 17, 2025 · 4 min di lettura

Proxies in JavaScript

How JavaScript Proxies work, some code examples, and how to use them for reactivity in JS

Condividi:XLinkedInFacebookWhatsApp

Proxies in JavaScript, what they are

In JavaScript, you often need to intercept and modify the behavior of objects, whether to add logging or validation functionality, or to implement security mechanisms. Traditionally this was done with getters and setters, but those approaches have limitations. Proxies offer a more flexible and powerful way to intercept operations on objects.

How Proxies work

A Proxy in JavaScript is a special object that wraps another object and intercepts its operations, such as reading, writing, and deleting properties. This happens through handlers and traps — functions that let you customize the object’s default behavior.

A simple example:

const target = { message: "Hello" };
const handler = {
  get: (obj, prop) => {
    console.log(`Accessing property: ${prop}`);
    return obj[prop];
  },
};
const proxy = new Proxy(target, handler);
console.log(proxy.message); // Prints "Hello" and logs the property access

In the example above we used one of the available traps (no, not the “music genre” 🙃), namely get, which lets us run a function whenever we access one of the object’s properties. What are the others?

Traps and how they’re used

Traps are special methods defined in the Proxy’s handler. Some of the most common ones include:

  • get(target, prop, receiver): intercepts property access.
  • set(target, prop, value, receiver): intercepts value assignment.
  • deleteProperty(target, prop): intercepts property deletion.
  • has(target, prop): intercepts use of the in operator.
  • apply(target, thisArg, argumentsList): intercepts function calls.
  • construct(target, args, newTarget): intercepts object creation with new.

Each trap lets you deeply customize an object’s behavior, making Proxies very powerful tools.

Use cases for Proxies

There’s a wide range of use cases; let’s look at a few to better understand how they work.

Automatic logging

You want to automatically log every access to and modification of an object.

Example

const target = { name: "Alice" };
const handler = {
  get: (obj, prop) => {
    console.log(`Reading ${prop}: ${obj[prop]}`);
    return obj[prop];
  },
  set: (obj, prop, value) => {
    console.log(`Setting ${prop}: ${value}`);
    obj[prop] = value;
    return true;
  },
};
const proxy = new Proxy(target, handler);
proxy.name; // Log: Reading name: Alice
proxy.name = "Bob"; // Log: Setting name: Bob

The proxy intercepts property access and modification, logging the details to the console.

Preventing invalid value assignment on an object

const secureObject = { secret: "12345", public: "info" };
const handler = {
  get: (obj, prop) => {
    if (prop === "secret") {
      throw new Error("Access denied");
    }
    return obj[prop];
  },
};
const proxy = new Proxy(secureObject, handler);
console.log(proxy.public); // OK
console.log(proxy.secret); // Error: Access denied

In this example, what we want to do is evaluate and potentially block access to a specific protected property. The proxy blocks access to secret, but allows reading public. Not bad, right?

When not to use Proxies

While Proxies are powerful, they’re not always the ideal solution. Some cases where it’s better to avoid them:

  • Performance-critical code: Using Proxies introduces overhead compared to normal objects.
  • Compatibility with older browsers: Not all browsers support Proxies, especially older ones (see Proxy on Can I Use)
  • Simple replacement of existing methods: If you just need to change a function’s behavior, you might use Object.defineProperty or direct wrappers instead.
  • Excessive complexity: Using a Proxy for simple scenarios can make the code less readable — it sounds obvious, but it’s always worth weighing these aspects.

A special case: reactivity with JavaScript

Proxies are often used in frontend frameworks, such as Vue.js, to implement data reactivity. This means that when a property of an object is modified, the system can automatically update the user interface.

Picture a scenario where you need to update an object in your frontend when an attribute of a state object changes. The code, greatly simplified, could look something like this.

const state = {
  count: 0,
};

const handler = {
  set: (obj, prop, value) => {
    console.log(`Updating ${prop}: ${value}`);
    obj[prop] = value;
    render(); // Simulated UI update
    return true;
  },
};

const reactiveState = new Proxy(state, handler);

function render() {
  console.log(`UI updated: count = ${reactiveState.count}`); // log for testing
  // this is where you'd update the relevant element in your DOM
}

reactiveState.count = 1; // Log: Updating count: 1, UI updated: count = 1
reactiveState.count = 2; // Log: Updating count: 2, UI updated: count = 2

The proxy intercepts changes to the count property and automatically calls the render function, simulating a UI update.

Conclusions

Proxies in JavaScript are really powerful. Of course, like everything, they should be used where it makes sense and where we can actually benefit from their characteristics.

If you’re interested in more advanced JavaScript features, also check out the guide to decorators.

Happy coding! 😃

References

Condividi:XLinkedInFacebookWhatsApp