Proxy
is a wrapper around an object, that forwards operations on it to the object, optionally trapping some of them. The Proxy object allows you to create an object that can be used in place of the original object, but which may redefine fundamental Object operations like getting, setting, and defining properties.

Reflect
API is designed to complement Proxy. Internal methods such as [[Get]]
, [[Set]]
and others are specification-only, can’t be called directly. The Reflect
object makes that somewhat possible.
So Proxy
is a wrapper which can be used to intercept fundamental operations like [[Get]]
and [[Set]]
on an object whereas Reflect
provides us minimal wrappers around these fundamental operations like [[Get]]
and [[Set]]
so that we can call them directly (Usually from inside the trap).
------------ How Reflect complements Proxy ------------
For every internal method, trappable by Proxy
, there’s a corresponding method in Reflect
, with the same name and arguments as the Proxy trap. (!Important)
Let's see this example to demonstrate how it is useful.
let user = {
_name: "Guest",
get name() {
return this._name;
}
};
let userProxy = new Proxy(user, {
get(target, prop, receiver) {
return Reflect.get(target, prop, receiver);
// return target[prop];
}
});
alert(userProxy.name); // Guest
In the above example, inside the get
trap, both return Reflect.get(target, prop, receiver);
and return target[prop];
will print the same output (Guest
).
Lets take an example that is little bit more complex to demonstrate why Reflect.get
is better and why get/set
have the third argument receiver
.
Lets create an object admin
which inherits from user
:
let user = {
_name: "Guest",
get name() {
return this._name;
}
};
let userProxy = new Proxy(user, {
get(target, prop, receiver) {
return target[prop]; // (*) target = user
}
});
let admin = {
__proto__: userProxy,
_name: "Admin"
};
// Expected: Admin
alert(admin.name); // outputs: Guest (?!?)
Reading admin.name
should return "Admin"
, not "Guest"
!
The problem is actually in the proxy, in the line (*)
.
- When we read
admin.name
, as admin
object doesn’t have such own property, the search goes to its prototype.
- The prototype is
userProxy
.
- When reading
name
property from the proxy, its get
trap triggers and returns it from the original object as target[prop]
in the line (*)
. A call to target[prop]
, when prop
is a getter, runs its code in the context this=target
. So the result is this._name
from the original object target
, that is: from user
.
To fix this we need to pass correct this
to the getter. receiver
, the third argument of get trap keeps the correct this
to be passed to a getter (in our case that’s admin). For a regular function we can use call/apply
to bind this
value, but we cannot do the same for getter
as it is not called
, just accessed.
This is where Reflect
is usefull. Remember, for every internal method, trappable by Proxy
, there’s a corresponding method in Reflect
, with the same name and arguments as the Proxy trap.
let user = {
_name: "Guest",
get name() {
return this._name;
}
};
let userProxy = new Proxy(user, {
get(target, prop, receiver) { // receiver = admin
return Reflect.get(target, prop, receiver); // (*)
}
});
let admin = {
__proto__: userProxy,
_name: "Admin"
};
alert(admin.name); // Admin
For detailed explanation please refer to this wonderful article by Ilya Kantor : Proxy and Reflect