In JavaScript, a Proxy is a powerful and flexible mechanism that allows you to define custom behavior for fundamental operations on objects. These operations can include reading, writing, updating, and deleting properties, as well as handling function invocations, among others.
JavaScript Proxies were introduced in ECMAScript 6 (ES6), offering a way to interact with objects in a more controlled manner. This allows developers to intercept and redefine how operations on objects are performed.
A Proxy is an object that wraps another object or function and intercepts its interactions (like method calls, property lookups, and assignments). This interception allows you to modify or extend the behavior of the object without altering the original implementation.
let handler = {
get: function(target, prop, receiver) {
console.log(`Getting property ${prop}`);
return prop in target ? target[prop] : `Property ${prop} not found`;
},
set: function(target, prop, value) {
console.log(`Setting property ${prop} with value ${value}`);
target[prop] = value;
return true;
}
};
let proxy = new Proxy(targetObject, handler);
target
: The original object that the Proxy is wrapping.handler
: An object that defines the traps (custom behaviors) for operations like getting or setting properties.A Proxy allows you to intercept various operations. The most common ones are:
get
: Intercepts the reading of a property.set
: Intercepts setting a property’s value.has
: Intercepts checking the existence of a property.deleteProperty
: Intercepts the deletion of a property.apply
: Intercepts function calls.construct
: Intercepts object construction via the new
keyword.In this simple example, we will create a Proxy to log whenever a property is accessed or modified in an object.
let person = {
name: 'Alice',
age: 30
};
let handler = {
get: function(target, prop) {
console.log(`Getting ${prop}`);
return prop in target ? target[prop] : `Property ${prop} not found`;
},
set: function(target, prop, value) {
console.log(`Setting ${prop} to ${value}`);
target[prop] = value;
return true; // Indicate that the set was successful
}
};
let proxyPerson = new Proxy(person, handler);
console.log(proxyPerson.name); // Output: Getting name, Alice
proxyPerson.age = 31; // Output: Setting age to 31
console.log(proxyPerson.age); // Output: Getting age, 31
proxyPerson.name
is accessed, the get
trap logs the access and retrieves the value of name
.proxyPerson.age = 31
is executed, the set
trap logs the change and updates the age
property.set
TrapIn this example, we will use a Proxy to enforce rules when setting properties on an object. For instance, we can prevent setting a negative value for age
.
let person = {
name: 'John',
age: 25
};
let handler = {
set: function(target, prop, value) {
if (prop === 'age' && value < 0) {
console.log('Age cannot be negative!');
return false; // Prevent the change
}
target[prop] = value;
return true; // Indicate success
}
};
let proxyPerson = new Proxy(person, handler);
proxyPerson.age = -5; // Output: Age cannot be negative!
console.log(proxyPerson.age); // Output: 25
proxyPerson.age = 30; // Age is successfully updated
console.log(proxyPerson.age); // Output: 30
set
trap checks if the property being modified is age
and if the new value is negative. If so, it prevents the update and logs an error.Proxies are not limited to objects; they can also be used with functions to intercept function calls. In this example, we will use a Proxy to log every time a function is called.
function greet(name) {
return `Hello, ${name}!`;
}
let handler = {
apply: function(target, thisArg, argumentsList) {
console.log(`Calling function with arguments: ${argumentsList}`);
return target(...argumentsList); // Call the original function
}
};
let proxyGreet = new Proxy(greet, handler);
console.log(proxyGreet('Alice')); // Output: Calling function with arguments: Alice, Hello, Alice!
apply
trap intercepts the function call. We log the arguments and then call the original greet
function with those arguments.Another useful feature of proxies is the ability to intercept the in
operator, which checks if a property exists in an object.
has
Trap
let person = {
name: 'Bob',
age: 40
};
let handler = {
has: function(target, prop) {
if (prop === 'age') {
console.log('Age property is being checked');
}
return prop in target;
}
};
let proxyPerson = new Proxy(person, handler);
console.log('name' in proxyPerson); // Output: true
console.log('age' in proxyPerson); // Output: Age property is being checked, true
console.log('height' in proxyPerson); // Output: false
has
trap is called whenever the in
operator is used to check the existence of a property in the proxy object.