JavaScript Proxy and Reflection

A detailed guide on Proxy and Reflection in JavaScript, covering their functionality, use cases, and practical examples

Last updated: 2024-12-23

Today, we're going to dive into two of JavaScript's most intriguing and powerful features - Proxy and Reflection. I'll try to explain this topic using simple and understandable examples. So, fasten your seatbelts, and let's embark on a journey into this fascinating part of the JavaScript world!

Proxy: Setting Up a "Controller" Over Objects

Proxy is a JavaScript feature that allows us to establish control over objects. Think of it as setting up a "guard" around your object. This guard monitors access to the object, exit from it, and any changes made to it.

Creating a Proxy

To create a Proxy, we use the Proxy constructor. It takes two arguments: the target object and a handler object.

const targetObject = { name: "Alice", age: 30 };
const handler = {
  get: function(target, prop) {
    console.log(`Property ${prop} is being read`);
    return target[prop];
  }
};

const proxy = new Proxy(targetObject, handler);

console.log(proxy.name); 
// Output:
// Property name is being read
// Alice

In this example, we created a proxy for accessing targetObject. Every time we access any property of the object, our "guard" (handler) reports about it.

Handler Methods

The handler object can include several methods. The most commonly used ones are:

  1. get: For reading a property
  2. set: For setting a property value
  3. has: For the in operator
  4. deleteProperty: For the delete operator

Let's add the set method:

const handler = {
  get: function(target, prop) {
    console.log(`Property ${prop} is being read`);
    return target[prop];
  },
  set: function(target, prop, value) {
    console.log(`Property ${prop} is being changed to ${value}`);
    target[prop] = value;
    return true;
  }
};

const proxy = new Proxy(targetObject, handler);

proxy.age = 31;
// Output: Property age is being changed to 31

console.log(proxy.age);
// Output:
// Property age is being read
// 31

Reflection: A New Way of Working with Objects

Reflection is a new API added to JavaScript for working with objects. It's closely related to Proxy and is often used in conjunction with it.

Reflection Methods

The Reflect object provides a number of useful methods. Most of them correspond to the Proxy handler methods:

  1. Reflect.get(target, propertyKey [, receiver])
  2. Reflect.set(target, propertyKey, value [, receiver])
  3. Reflect.has(target, propertyKey)
  4. Reflect.deleteProperty(target, propertyKey)

Let's see Reflection in action:

const obj = { name: "Bob", age: 25 };

console.log(Reflect.get(obj, 'name')); // Output: Bob

Reflect.set(obj, 'job', 'Developer');
console.log(obj.job); // Output: Developer

console.log(Reflect.has(obj, 'age')); // Output: true

Reflect.deleteProperty(obj, 'age');
console.log(obj.age); // Output: undefined

Proxy and Reflection Together

Using Proxy and Reflection together yields powerful results. Let's look at an example:

const targetObject = { name: "Charlie", age: 40 };

const handler = {
  get(target, prop, receiver) {
    if (prop === 'age') {
      return `${Reflect.get(target, prop, receiver)} years old`;
    }
    return Reflect.get(target, prop, receiver);
  },
  set(target, prop, value, receiver) {
    if (prop === 'age' && typeof value !== 'number') {
      throw new TypeError("Age must be a number");
    }
    return Reflect.set(target, prop, value, receiver);
  }
};

const proxy = new Proxy(targetObject, handler);

console.log(proxy.name); // Output: Charlie
console.log(proxy.age); // Output: 40 years old

proxy.age = 41;
console.log(proxy.age); // Output: 41 years old

try {
  proxy.age = "forty two";
} catch (error) {
  console.error(error.message); // Output: Age must be a number
}

In this example, we used Proxy and Reflection together to handle the age property specially. We added "years old" when reading the age through the get method, and ensured that only numeric values can be set for age through the set method.

Practical Use Cases

Proxy and Reflection can be useful in many scenarios:

  1. Property Validation: Checking and validating values assigned to object properties.
  2. Write Protection: Making certain properties of an object immutable.
  3. Property Hiding: Making some properties invisible from outside.
  4. Caching: Improving performance by caching results.
  5. Logging: Logging and monitoring interactions with an object.

For instance, let's create a simple caching system:

function cachingProxy(target, cache = new Map()) {
  return new Proxy(target, {
    apply(target, thisArg, argArray) {
      const key = argArray.toString();
      if (cache.has(key)) {
        console.log('Fetching from cache');
        return cache.get(key);
      }
      const result = Reflect.apply(target, thisArg, argArray);
      cache.set(key, result);
      return result;
    }
  });
}

function expensiveCalculation(x, y) {
  console.log('Performing calculation...');
  return x + y;
}

const cachedCalculation = cachingProxy(expensiveCalculation);

console.log(cachedCalculation(5, 3)); // Performing calculation... 8
console.log(cachedCalculation(5, 3)); // Fetching from cache 8

In this example, we created a caching system for function results using Proxy. When called with the same arguments again, the result is fetched from the cache, improving performance.

Conclusion

Proxy and Reflection are powerful features of JavaScript that give us complete control over objects. With them, we can enhance security, improve performance, and add new functionalities.

However, like any powerful tool, they should be used with caution. If used incorrectly, they can increase code complexity and negatively impact performance.

By learning and applying Proxy and Reflection, you'll take your JavaScript skills to a new level. They open up new ways of working with objects and make your code more flexible and powerful.

Enjoy coding, and remember - every new technology you learn makes you a stronger developer!

Additional Resources

  1. MDN Web Docs: Proxy
  2. MDN Web Docs: Reflection
  3. JavaScript.info: Proxy and Reflect