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-23Today, 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:
get
: For reading a propertyset
: For setting a property valuehas
: For thein
operatordeleteProperty
: For thedelete
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:
Reflect.get(target, propertyKey [, receiver])
Reflect.set(target, propertyKey, value [, receiver])
Reflect.has(target, propertyKey)
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:
- Property Validation: Checking and validating values assigned to object properties.
- Write Protection: Making certain properties of an object immutable.
- Property Hiding: Making some properties invisible from outside.
- Caching: Improving performance by caching results.
- 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!