JavaScript - JavaScript Proxies and the Reflect API
Introduction
JavaScript Proxies and the Reflect API are advanced features that allow developers to control and customize how objects and functions behave.
Normally, when you access an object property, assign a value, delete a property, or call a function, JavaScript performs the operation directly. With a Proxy, you can intercept these operations and execute your own logic before, after, or instead of the normal operation.
The Reflect API provides standard methods for performing these operations programmatically.
This topic is useful for understanding object validation, access control, logging, reactive systems, and advanced JavaScript frameworks.
1. What is a JavaScript Proxy?
A Proxy is an object that acts as a wrapper around another object or function. It allows you to intercept and customize operations performed on the original object.
The object being wrapped is called the target.
The Proxy uses a special object called a handler to define what happens when an operation is performed.
Basic syntax
JavaScript
const proxy = new Proxy(target, handler);
Where:
-
target: The original object or function that you want to wrap. -
handler: An object containing methods called traps. -
proxy: The new object through which operations are intercepted.
Example
JavaScript
const student = {
name: "Rahul",
age: 21
};
const handler = {
get(target, property) {
console.log("Property accessed:", property);
return target[property];
}
};
const proxyStudent = new Proxy(student, handler);
console.log(proxyStudent.name);
Output
Property accessed: name
Rahul
Explanation
-
The
studentobject containsnameandage. -
A Proxy is created around the
studentobject. -
The
get()trap intercepts property access. -
When
proxyStudent.nameis executed, theget()method runs. -
The property value
"Rahul"is returned.
The Proxy does not automatically modify the original object. It provides a controlled way to interact with it.
2. Why are Proxies useful?
Proxies are useful when you want to control or monitor object operations.
Common applications include:
Data validation
You can prevent invalid values from being assigned to an object.
For example, an employee's salary should not be negative.
Logging
You can record when a property is accessed, modified, or deleted.
Access control
You can restrict access to certain properties.
Reactive programming
You can detect changes to objects and trigger updates in an application.
Custom object behavior
You can change how objects respond to operations such as property access, assignment, and function calls.
3. What are Proxy Traps?
A Proxy trap is a special method inside the handler object that intercepts a particular JavaScript operation.
Each trap corresponds to an operation performed on the target.
For example:
|
Trap
|
Operation intercepted
|
| --- | --- |
|
get()
|
Reading a property
|
|
set()
|
Assigning a property
|
|
has()
|
Checking whether a property exists
|
|
deleteProperty()
|
Deleting a property
|
|
ownKeys()
|
Retrieving property keys
|
|
getOwnPropertyDescriptor()
|
Getting a property descriptor
|
|
defineProperty()
|
Defining a property
|
|
getPrototypeOf()
|
Getting the prototype
|
|
setPrototypeOf()
|
Setting the prototype
|
|
isExtensible()
|
Checking extensibility
|
|
preventExtensions()
|
Preventing extensions
|
|
apply()
|
Calling a function
|
|
construct()
|
Using a function with new
|
A handler does not need to define every trap. If a trap is missing, the operation generally behaves like the corresponding operation on the target.
4. The get() Trap
The get() trap is executed when a property is read through a Proxy.
Syntax
JavaScript
get(target, property, receiver) {
// Custom logic
}
Parameters
-
target: The original object. -
property: The property name being accessed. -
receiver: The object on which the property access originated, usually the Proxy.
Example
JavaScript
const product = {
name: "Laptop",
price: 50000
};
const handler = {
get(target, property) {
console.log("Reading property:", property);
return target[property];
}
};
const proxyProduct = new Proxy(product, handler);
console.log(proxyProduct.name);
console.log(proxyProduct.price);
Output
Reading property: name
Laptop
Reading property: price
50000
Explanation
When proxyProduct.name is accessed, the Proxy invokes the get() trap.
The trap receives:
JavaScript
target = product
property = "name"
The expression:
JavaScript
return target[property];
returns the actual property value.
Important point
The get() trap runs when accessing properties through the Proxy, not when directly accessing the original object.
JavaScript
console.log(product.name);
This direct access does not use the Proxy's get() trap.
5. The set() Trap
The set() trap is used to intercept property assignments.
It runs when a value is assigned through a Proxy.
Syntax
JavaScript
set(target, property, value, receiver) {
// Custom logic
}
Parameters
-
target: The original object. -
property: The property being modified. -
value: The new value. -
receiver: The Proxy or object through which the assignment occurs.
Example: Logging assignments
JavaScript
const employee = {
name: "Anita",
salary: 30000
};
const handler = {
set(target, property, value) {
console.log("Updating:", property, "to", value);
target[property] = value;
return true;
}
};
const proxyEmployee = new Proxy(employee, handler);
proxyEmployee.salary = 40000;
console.log(proxyEmployee.salary);
Output
Updating: salary to 40000
40000
Explanation
When this statement runs:
JavaScript
proxyEmployee.salary = 40000;
The set() trap is called.
The property is updated using:
JavaScript
target[property] = value;
The trap returns true to indicate that the assignment was successful.
Why must set() return true or false?
A set() trap must return a Boolean-compatible result.
-
true: The assignment is reported as successful. -
false: The assignment is reported as unsuccessful.
In strict mode, returning false from a failed assignment can cause a TypeError.
6. Data Validation Using Proxy
One of the most practical uses of a Proxy is validating data before storing it.
Suppose an employee's salary must be a positive number.
Example
JavaScript
const employee = {
name: "Ravi",
salary: 30000
};
const handler = {
set(target, property, value) {
if (property === "salary") {
if (typeof value !== "number" || value < 0) {
throw new Error("Salary must be a positive number");
}
}
target[property] = value;
return true;
}
};
const proxyEmployee = new Proxy(employee, handler);
proxyEmployee.salary = 45000;
console.log(proxyEmployee.salary);
Output
45000
If you try:
JavaScript
proxyEmployee.salary = -5000;
The following error is thrown:
Error: Salary must be a positive number
Explanation
-
The assignment is intercepted by
set(). -
The trap checks whether the property is
salary. -
It checks whether the new value is a number.
-
It checks whether the value is less than zero.
-
If invalid, an error is thrown.
-
If valid, the value is assigned.
This allows validation logic to be centralized in one place.
7. The has() Trap
The has() trap intercepts the in operator.
Syntax
JavaScript
has(target, property) {
// Custom logic
}
Example
JavaScript
const student = {
name: "Priya",
age: 22
};
const handler = {
has(target, property) {
console.log("Checking:", property);
return property in target;
}
};
const proxyStudent = new Proxy(student, handler);
console.log("name" in proxyStudent);
console.log("marks" in proxyStudent);
Output
Checking: name
true
Checking: marks
false
Explanation
The expression:
JavaScript
"name" in proxyStudent
calls the has() trap.
The expression:
JavaScript
property in target
checks whether the property exists in the original object or its prototype chain.
8. The deleteProperty() Trap
The deleteProperty() trap intercepts the delete operator.
Syntax
JavaScript
deleteProperty(target, property) {
// Custom logic
}
Example
JavaScript
const user = {
name: "Kiran",
age: 25
};
const handler = {
deleteProperty(target, property) {
console.log("Deleting:", property);
delete target[property];
return true;
}
};
const proxyUser = new Proxy(user, handler);
delete proxyUser.age;
console.log(proxyUser);
Output
JavaScript
{
name: "Kiran"
}
Explanation
When this statement executes:
JavaScript
delete proxyUser.age;
The deleteProperty() trap is called.
The property is deleted from the target object.
Practical use
This trap can be used to:
-
Prevent deletion of important properties.
-
Record deleted data.
-
Apply custom rules before removing properties.
9. The ownKeys() Trap
The ownKeys() trap intercepts operations that retrieve an object's own property keys.
Examples include:
JavaScript
Object.keys()
Object.getOwnPropertyNames()
Reflect.ownKeys()
Example
JavaScript
const user = {
name: "Meena",
age: 28,
city: "Bengaluru"
};
const handler = {
ownKeys(target) {
return ["name", "city"];
}
};
const proxyUser = new Proxy(user, handler);
console.log(Object.keys(proxyUser));
Output
["name", "city"]
Explanation
The target contains three properties:
JavaScript
name
age
city
However, the ownKeys() trap returns only:
JavaScript
["name", "city"]
Therefore, those are the keys returned by Object.keys(proxyUser).
Important restriction
The ownKeys() trap must follow JavaScript Proxy invariants. For example, it cannot arbitrarily hide certain non-configurable properties or omit required keys from a non-extensible target.
10. The apply() Trap
The apply() trap is used when the target is a function.
It intercepts function calls.
Syntax
JavaScript
apply(target, thisArg, argumentsList) {
// Custom logic
}
Parameters
-
target: The original function. -
thisArg: The value used asthis. -
argumentsList: The arguments passed to the function.
Example
JavaScript
function add(a, b) {
return a + b;
}
const handler = {
apply(target, thisArg, argumentsList) {
console.log("Function called");
return target.apply(thisArg, argumentsList);
}
};
const proxyAdd = new Proxy(add, handler);
console.log(proxyAdd(10, 20));
Output
Function called
30
Explanation
When proxyAdd(10, 20) runs:
-
The Proxy intercepts the function call.
-
The
apply()trap executes. -
The original function is called using
target.apply(). -
The result is returned.
Practical uses
The apply() trap is useful for:
-
Function logging.
-
Measuring execution time.
-
Adding access checks.
-
Creating function wrappers.
-
Tracking function calls.
11. The construct() Trap
The construct() trap intercepts the new operator.
It is used when the target is a constructor function.
Example
JavaScript
function Person(name) {
this.name = name;
}
const handler = {
construct(target, argumentsList, newTarget) {
console.log("Creating a new Person");
return Reflect.construct(
target,
argumentsList,
newTarget
);
}
};
const ProxyPerson = new Proxy(Person, handler);
const person = new ProxyPerson("Arun");
console.log(person.name);
Output
Creating a new Person
Arun
Explanation
When:
JavaScript
new ProxyPerson("Arun");
is executed, the construct() trap is called.
The original constructor is invoked using:
JavaScript
Reflect.construct(target, argumentsList, newTarget);
The result is a newly created object.
12. What is the Reflect API?
The Reflect API is a built-in JavaScript object that provides methods for performing fundamental object operations.
It was introduced in ECMAScript 2015 (ES6).
Reflect methods are often used inside Proxy traps because they provide a standard way to forward operations to the target.
Common Reflect methods
|
Reflect method
|
Purpose
|
| --- | --- |
|
Reflect.get()
|
Read a property
|
|
Reflect.set()
|
Set a property
|
|
Reflect.has()
|
Check whether a property exists
|
|
Reflect.deleteProperty()
|
Delete a property
|
|
Reflect.ownKeys()
|
Get property keys
|
|
Reflect.getOwnPropertyDescriptor()
|
Get a property descriptor
|
|
Reflect.defineProperty()
|
Define a property
|
|
Reflect.getPrototypeOf()
|
Get the prototype
|
|
Reflect.setPrototypeOf()
|
Set the prototype
|
|
Reflect.isExtensible()
|
Check extensibility
|
|
Reflect.preventExtensions()
|
Prevent extensions
|
|
Reflect.apply()
|
Call a function
|
|
Reflect.construct()
|
Construct an object
|
Reflect does not create a Proxy. It provides methods for performing operations that a Proxy can intercept.
13. Reflect.get()
Reflect.get() retrieves a property value from an object.
Syntax
JavaScript
Reflect.get(target, property, receiver);
The receiver parameter is optional.
Example
JavaScript
const student = {
name: "Suresh",
age: 23
};
console.log(Reflect.get(student, "name"));
console.log(Reflect.get(student, "age"));
Output
Suresh
23
Equivalent operation
JavaScript
student.name
is similar to:
JavaScript
Reflect.get(student, "name");
Using Reflect.get() inside a Proxy
JavaScript
const student = {
name: "Suresh",
age: 23
};
const handler = {
get(target, property, receiver) {
console.log("Accessing:", property);
return Reflect.get(target, property, receiver);
}
};
const proxyStudent = new Proxy(student, handler);
console.log(proxyStudent.name);
Output
Accessing: name
Suresh
Why use Reflect.get()?
Instead of manually writing:
JavaScript
return target[property];
you can write:
JavaScript
return Reflect.get(target, property, receiver);
This is especially useful when working with inheritance, getters, and the receiver object.
14. Reflect.set()
Reflect.set() assigns a value to an object's property.
Syntax
JavaScript
Reflect.set(target, property, value, receiver);
Example
JavaScript
const employee = {
name: "Asha",
salary: 25000
};
const result = Reflect.set(employee, "salary", 35000);
console.log(result);
console.log(employee.salary);
Output
true
35000
Explanation
The method:
JavaScript
Reflect.set(employee, "salary", 35000);
assigns 35000 to the salary property.
It returns true when the assignment succeeds.
Using Reflect.set() in a Proxy
JavaScript
const employee = {
name: "Asha",
salary: 25000
};
const handler = {
set(target, property, value, receiver) {
console.log("Setting:", property);
return Reflect.set(
target,
property,
value,
receiver
);
}
};
const proxyEmployee = new Proxy(employee, handler);
proxyEmployee.salary = 35000;
console.log(proxyEmployee.salary);
Output
Setting: salary
35000
Reflect.set() is often preferred in a Proxy because it forwards the assignment while preserving the correct receiver behavior.
15. Reflect.has()
Reflect.has() checks whether a property exists in an object.
Syntax
JavaScript
Reflect.has(target, property);
Example
JavaScript
const product = {
name: "Mobile",
price: 15000
};
console.log(Reflect.has(product, "name"));
console.log(Reflect.has(product, "brand"));
Output
true
false
Equivalent operation
JavaScript
"name" in product
is similar to:
JavaScript
Reflect.has(product, "name");
Using it in a Proxy
JavaScript
const product = {
name: "Mobile",
price: 15000
};
const handler = {
has(target, property) {
console.log("Checking property:", property);
return Reflect.has(target, property);
}
};
const proxyProduct = new Proxy(product, handler);
console.log("price" in proxyProduct);
Output
Checking property: price
true
16. Reflect.deleteProperty()
This method deletes a property from an object.
Syntax
JavaScript
Reflect.deleteProperty(target, property);
Example
JavaScript
const student = {
name: "Rohan",
age: 20
};
const result = Reflect.deleteProperty(student, "age");
console.log(result);
console.log(student);
Output
true
{
name: "