Intercepting Object Operations

Introduction: Why Intercept Object Operations?

Welcome to the first lesson of our course on Meta-Programming and Advanced Integration Patterns. In this course, we are going to explore how to write code that treats other code as data. This is a powerful skill that allows you to create more flexible and dynamic applications.

In standard programming, we usually interact with objects directly. We read a property, change a value, or delete a key. Meta-programming changes this by allowing us to step in the middle of these actions. This is called "interception." By intercepting these operations, we can add extra logic without changing the original object or the code that uses it.

In the real world, this is very useful for several reasons. You might want to log every time a piece of data is changed for debugging purposes. You could also use it to validate data, ensuring that a user does not set a price property to a negative number. It is also great for access control, where you can prevent certain parts of your program from reading sensitive information. In this lesson, we will focus on building a logging tool that tracks everything happening to an object.

The Proxy Object: Wrapping A Target

To start intercepting operations, JavaScript provides us with a special object called a Proxy. You can think of a Proxy as a wrapper or a "middleman" for another object. The object being wrapped is called the target. Once an object is wrapped, any interaction with that object must go through the Proxy first.

To create a Proxy, we use the new Proxy(target, handler) syntax. The target is the original object we want to watch. The handler is another object that contains our "traps."

const user = { name: "Alice" };
const handler = {}; // No traps defined yet
const proxyUser = new Proxy(user, handler);

console.log(proxyUser.name); // Outputs: Alice

If we don't put any traps in the handler, the Proxy will just pass everything through to the target as if it weren't there.

Intercepting Property Access With Get And Set Traps

The most common things we do with objects are reading and writing values. The get trap intercepts any attempt to read a property. The set trap intercepts any attempt to change a property value.

const handler = {
  get(target, prop) {
    console.log(`Property "${String(prop)}" was accessed.`);
    return target[prop];
  },
  set(target, prop, value) {
    if (prop === "age" && value < 0) {
      // Reject the write by throwing. See the note below on why we
      // throw instead of returning false.
      throw new TypeError("Age cannot be negative!");
    }
    target[prop] = value;
    return true;
  }
};

const proxy = new Proxy({ age: 25 }, handler);
console.log(proxy.age); // Logs access, then prints 25
proxy.age = -5;         // Throws TypeError: Age cannot be negative!

The get trap receives the target and the prop. The set trap additionally receives the value someone is trying to save. By using these traps, we can see exactly what is being read and what is being changed in real-time.

A quick but important detail: a set trap must return a boolean. Returning true means "the write succeeded," and returning false means "the write was rejected." However, in strict mode — which is always on inside JavaScript modules and any file with "use strict" — returning false from a set trap does not fail quietly. Instead, the assignment expression throws a TypeError. Because all the code in this course runs as strict-mode module code, a rejected assignment will surface as a thrown error. For that reason, when we want to reject a write with a clear, custom message, we prefer to throw new TypeError(...) explicitly, as shown above. We still return true for writes that we want to allow.

Notice that we wrap the property key with String(prop) before logging it. A property key can be a string or a Symbol, and string methods are not available on symbols. Converting with String(prop) keeps our traps safe no matter what kind of key is used.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal