Proxy_New

Proxy – JavaScript – MDN Web Docs

The Proxy object enables you to create a proxy for another object, which can intercept and redefine fundamental operations for that scriptionA Proxy is created with two parameters:
target: the original object which you want to proxy
handler: an object that defines which operations will be intercepted and how to redefine intercepted operations.
For example, this code defines a simple target with just two properties, and an even simpler handler with no properties:
const target = {
message1: “hello”,
message2: “everyone”};
const handler1 = {};
const proxy1 = new Proxy(target, handler1);
Because the handler is empty, this proxy behaves just like the original target:
(ssage1); // hello
(ssage2); // everyone
To customise the proxy, we define functions on the handler object:
const handler2 = {
get: function(target, prop, receiver) {
return “world”;}};
const proxy2 = new Proxy(target, handler2);
Here we’ve provided an implementation of the get() handler, which intercepts attempts to access properties in the target.
Handler functions are sometimes called traps, presumably because they trap calls to the target object. The very simple trap in handler2 above redefines all property accessors:
(ssage1); // world
(ssage2); // world
With the help of the Reflect class we can give some accessors the original behavior and redefine others:
const handler3 = {
get: function (target, prop, receiver) {
if (prop === “message2”) {
return “world”;}
return (guments);}, };
const proxy3 = new Proxy(target, handler3);
Constructor
Proxy()
Creates a new Proxy object.
Static methodsExamplesBasic exampleIn this simple example, the number 37 gets returned as the default value when the property name is not in the object. It is using the get() handler.
const handler = {
get: function(obj, prop) {
return prop in obj?
obj[prop]:
37;}};
const p = new Proxy({}, handler);
p. a = 1;
p. b = undefined;
(p. a, p. b);
// 1, undefined
(‘c’ in p, p. c);
// false, 37
No-op forwarding proxyIn this example, we are using a native JavaScript object to which our proxy will forward all operations that are applied to it.
const target = {};
const p = new Proxy(target, {});
p. a = 37;
// operation forwarded to the target
(target. a);
// 37
// (The operation has been properly forwarded! )
Note that while this “no-op” works for JavaScript objects, it does not work for native browser objects like DOM lidationWith a Proxy, you can easily validate the passed value for an object. This example uses the set() handler.
let validator = {
set: function(obj, prop, value) {
if (prop === ‘age’) {
if (! Integer(value)) {
throw new TypeError(‘The age is not an integer’);}
if (value > 200) {
throw new RangeError(‘The age seems invalid’);}}
// The default behavior to store the value
obj[prop] = value;
// Indicate success
return true;}};
const person = new Proxy({}, validator);
= 100;
(); // 100
= ‘young’; // Throws an exception
= 300; // Throws an exception
Extending constructorA function proxy could easily extend a constructor with a new constructor. This example uses the construct() and apply() handlers.
function extend(sup, base) {
var descriptor = tOwnPropertyDescriptor(
ototype, ‘constructor’);
ototype = (ototype);
var handler = {
construct: function(target, args) {
var obj = (ototype);
(target, obj, args);
return obj;},
apply: function(target, that, args) {
(that, args);
(that, args);}};
var proxy = new Proxy(base, handler);
= proxy;
fineProperty(ototype, ‘constructor’, descriptor);
return proxy;}
var Person = function(name) {
= name;};
var Boy = extend(Person, function(name, age) {
= age;});
= ‘M’;
var Peter = new Boy(‘Peter’, 13);
(); // “M”
(); // “Peter”
(); // 13
Manipulating DOM nodesSometimes you want to toggle the attribute or class name of two different elements. Here’s how using the set() handler.
let view = new Proxy({
selected: null},
{
set: function(obj, prop, newval) {
let oldval = obj[prop];
if (prop === ‘selected’) {
if (oldval) {
tAttribute(‘aria-selected’, ‘false’);}
if (newval) {
tAttribute(‘aria-selected’, ‘true’);}}
obj[prop] = newval;
return true;}});
let i1 = lected = tElementById(‘item-1’); //giving error here, i1 is null
(tAttribute(‘aria-selected’));
// ‘true’
let i2 = lected = tElementById(‘item-2’);
// ‘false’
Note: even if selected:! null, then giving tAttribute is not a function
Value correction and an extra propertyThe products proxy object evaluates the passed value and converts it to an array if needed. The object also supports an extra property called latestBrowser both as a getter and a setter.
let products = new Proxy({
browsers: [‘Internet Explorer’, ‘Netscape’]},
// An extra property
if (prop === ‘latestBrowser’) {
return owsers[ – 1];}
// The default behavior to return the value
return obj[prop];},
(value);
return true;}
// Convert the value if it is not an array
if (typeof value === ‘string’) {
value = [value];}
(owsers);
// [‘Internet Explorer’, ‘Netscape’]
owsers = ‘Firefox’;
// pass a string (by mistake)
// [‘Firefox’] <- no problem, the value is an array testBrowser = 'Chrome'; // ['Firefox', 'Chrome'] (testBrowser); // 'Chrome' Finding an array item object by its propertyThis proxy extends an array with some utility features. As you see, you can flexibly "define" properties without using fineProperties(). This example can be adapted to find a table row by its cell. In that case, the target will be let products = new Proxy([ { name: 'Firefox', type: 'browser'}, { name: 'SeaMonkey', type: 'browser'}, { name: 'Thunderbird', type: 'mailer'}], // The default behavior to return the value; prop is usually an integer if (prop in obj) { return obj[prop];} // Get the number of products; an alias of if (prop === 'number') { return;} let result, types = {}; for (let product of obj) { if ( === prop) { result = product;} if (types[]) { types[](product);} else { types[] = [product];}} // Get a product by name if (result) { return result;} // Get products by type if (prop in types) { return types[prop];} // Get product types if (prop === 'types') { return (types);} return undefined;}}); (products[0]); // { name: 'Firefox', type: 'browser'} (products['Firefox']); // { name: 'Firefox', type: 'browser'} (products['Chrome']); // undefined (owser); // [{ name: 'Firefox', type: 'browser'}, { name: 'SeaMonkey', type: 'browser'}] (); // ['browser', 'mailer'] (); // 3 A complete traps list exampleNow in order to create a complete sample traps list, for didactic purposes, we will try to proxify a non-native object that is particularly suited to this type of operation: the docCookies global object created by a simple cookie framework. /* var docCookies =... get the "docCookies" object here: */ var docCookies = new Proxy(docCookies, { get: function (oTarget, sKey) { return oTarget[sKey] || tItem(sKey) || undefined;}, set: function (oTarget, sKey, vValue) { if (sKey in oTarget) { return false;} return tItem(sKey, vValue);}, deleteProperty: function (oTarget, sKey) { if (! sKey in oTarget) { return false;} return moveItem(sKey);}, enumerate: function (oTarget, sKey) { return ();}, ownKeys: function (oTarget, sKey) { has: function (oTarget, sKey) { return sKey in oTarget || oTarget. hasItem(sKey);}, defineProperty: function (oTarget, sKey, oDesc) { if (oDesc && 'value' in oDesc) { tItem(sKey, );} return oTarget;}, getOwnPropertyDescriptor: function (oTarget, sKey) { var vValue = tItem(sKey); return vValue? { value: vValue, writable: true, enumerable: true, configurable: false}: undefined;}, }); /* Cookies test */ (_cookie1 = 'First value'); (tItem('my_cookie1')); tItem('my_cookie1', 'Changed value'); (_cookie1); SpecificationsSpecificationECMAScript Language Specification (ECMAScript)# sec-proxy-objectsBrowser compatibilityBCD tables only load in the browserSee also "Proxies are awesome" Brendan Eich presentation at JSConf (slides) Tutorial on proxies JavaScript Proxy Explained Clearly By Practical Examples

JavaScript Proxy Explained Clearly By Practical Examples

Summary: in this tutorial, you will learn about the JavaScript Proxy object in is a JavaScript Proxy objectA JavaScript Proxy is an object that wraps another object (target) and intercepts the fundamental operations of the target fundamental operations can be the property lookup, assignment, enumeration, and function invocations, eating a proxy objectTo create a new proxy object, you use the following syntax:let proxy = new Proxy(target, handler);
Code language: JavaScript (javascript)In this syntax:target – is an object to wrap. handler – is an object that contains methods to control the behaviors of the target. The methods inside the handler object are called traps. A simple proxy exampleFirst, define an object called user:const user = {
firstName: ‘John’,
lastName: ‘Doe’,
email: ”, }
Code language: JavaScript (javascript)Second, define a handler object:const handler = {
get(target, property) {
(`Property ${property} has been read. `);
return target[property];}}
Code language: JavaScript (javascript)Third, create a proxy object:const proxyUser = new Proxy(user, handler);
Code language: JavaScript (javascript)The proxyUser object uses the user object to store data. The proxyUser can access all properties of the user, access the firstName and lastName properties of the user object via the proxyUser (rstName);
(stName);
Code language: CSS (css)Output:Property firstName has been read.
John
Property lastName has been read.
Doe
When you access a property of the user object via the proxyUser object, the get() method in the handler object is, if you modify the original object user, the change is reflected in the rstName = ‘Jane’;
(rstName);
Code language: JavaScript (javascript)Output:Property firstName has been read.
Jane
Similarly, a change in the proxyUser object will be reflected in the original object (user)stName = ‘William’;
Code language: JavaScript (javascript)Output:William
Proxy TrapsThe get() trapThe get() trap is fired when a property of the target object is accessed via the proxy the previous example, a message is printed out when a property of the user object is accessed by the proxyUser nerally, you can develop a custom logic in the get() trap when a property is example, you can use the get() trap to define computed properties for the target object. The computed properties are properties whose values are calculated based on values of existing user object does not have a property fullName, you can use the get() trap to create the fullName property based on the firstName and lastName properties:const user = {
lastName: ‘Doe’}
const handler = {
return property === ‘fullName’?
`${rstName} ${stName}`:
target[property];}};
const proxyUser = new Proxy(user, handler);
(proxyUser. fullName);
Code language: JavaScript (javascript)Output:John Doe
The set() trapThe set() trap controls behavior when a property of the target object is ppose that the age of user must be greater than 18. To enforce this constraint, you develop a set() trap as follows:const user = {
age: 20}
set(target, property, value) {
if (property === ‘age’) {
if (typeof value! == ‘number’) {
throw new Error(‘Age must be a number. ‘);}
if (value < 18) { throw new Error('The user must be 18 or older. ')}} target[property] = value;}}; Code language: JavaScript (javascript)First, set the age of user to a = 'foo'; Code language: JavaScript (javascript)Output:Error: Age must be a number. Code language: JavaScript (javascript)Second, set the age of the user to = '16'; Code language: JavaScript (javascript)Output:The user must be 18 or older. Third, set the age of the user to = 21; No error apply() trapThe () method is a trap for a function call. Here is the syntax:let proxy = new Proxy(target, { apply: function(target, thisArg, args) { //... }}); Code language: JavaScript (javascript)See the following example:const user = { const getFullName = function (user) { return `${rstName} ${stName}`;} const getFullNameProxy = new Proxy(getFullName, { apply(target, thisArg, args) { return target(.. ). toUpperCase();}}); (getFullNameProxy(user)); // Code language: JavaScript (javascript)OutputJOHN DOE More trapsThe following are more traps:construct – traps usage of the new operatorgetPrototypeOf – traps an internal call to [[GetPrototypeOf]]setPrototypeOf – traps a call to tPrototypeOfisExtensible – traps a call to ExtensiblepreventExtensions – traps a call to eventExtensionsgetOwnPropertyDescriptor – traps a call to tOwnPropertyDescriptorIn this tutorial, you have learned about the JavaScript Proxy object used to wrap another object to change the fundamental behaviors of that this tutorial helpful? Proxy and Reflect - The Modern JavaScript Tutorial

Proxy and Reflect – The Modern JavaScript Tutorial

A Proxy object wraps another object and intercepts operations, like reading/writing properties and others, optionally handling them on its own, or transparently allowing the object to handle them.
Proxies are used in many libraries and some browser frameworks. We’ll see many practical applications in this article.
ProxyThe syntax:
let proxy = new Proxy(target, handler)
target – is an object to wrap, can be anything, including functions.
handler – proxy configuration: an object with “traps”, methods that intercept operations. – e. g. get trap for reading a property of target, set trap for writing a property into target, and so on.
For operations on proxy, if there’s a corresponding trap in handler, then it runs, and the proxy has a chance to handle it, otherwise the operation is performed on target.
As a starting example, let’s create a proxy without any traps:
let target = {};
let proxy = new Proxy(target, {}); // empty handler
= 5; // writing to proxy (1)
alert(); // 5, the property appeared in target!
alert(); // 5, we can read it from proxy too (2)
for(let key in proxy) alert(key); // test, iteration works (3)
As there are no traps, all operations on proxy are forwarded to target.
A writing operation sets the value on target.
A reading operation returns the value from target.
Iteration over proxy returns values from target.
As we can see, without any traps, proxy is a transparent wrapper around target.
Proxy is a special “exotic object”. It doesn’t have own properties. With an empty handler it transparently forwards operations to target.
To activate more capabilities, let’s add traps.
What can we intercept with them?
For most operations on objects, there’s a so-called “internal method” in the JavaScript specification that describes how it works at the lowest level. For instance [[Get]], the internal method to read a property, [[Set]], the internal method to write a property, and so on. These methods are only used in the specification, we can’t call them directly by name.
Proxy traps intercept invocations of these methods. They are listed in the Proxy specification and in the table below.
For every internal method, there’s a trap in this table: the name of the method that we can add to the handler parameter of new Proxy to intercept the operation:
Internal Method
Handler Method
Triggers when…
[[Get]]
get
reading a property
[[Set]]
set
writing to a property
[[HasProperty]]
has
in operator
[[Delete]]
deleteProperty
delete operator
[[Call]]
apply
function call
[[Construct]]
construct
new operator
[[GetPrototypeOf]]
getPrototypeOf
tPrototypeOf
[[SetPrototypeOf]]
setPrototypeOf
[[IsExtensible]]
isExtensible
Extensible
[[PreventExtensions]]
preventExtensions
eventExtensions
[[DefineOwnProperty]]
defineProperty
fineProperty, fineProperties
[[GetOwnProperty]]
getOwnPropertyDescriptor
tOwnPropertyDescriptor,,
[[OwnPropertyKeys]]
ownKeys
tOwnPropertyNames, tOwnPropertySymbols,,
Invariants
JavaScript enforces some invariants – conditions that must be fulfilled by internal methods and traps.
Most of them are for return values:
[[Set]] must return true if the value was written successfully, otherwise false.
[[Delete]] must return true if the value was deleted successfully, otherwise false.
…and so on, we’ll see more in examples below.
There are some other invariants, like:
[[GetPrototypeOf]], applied to the proxy object must return the same value as [[GetPrototypeOf]] applied to the proxy object’s target object. In other words, reading prototype of a proxy must always return the prototype of the target object.
Traps can intercept these operations, but they must follow these rules.
Invariants ensure correct and consistent behavior of language features. The full invariants list is in the specification. You probably won’t violate them if you’re not doing something weird.
Let’s see how that works in practical examples.
Default value with “get” trapThe most common traps are for reading/writing properties.
To intercept reading, the handler should have a method get(target, property, receiver).
It triggers when a property is read, with following arguments:
target – is the target object, the one passed as the first argument to new Proxy,
property – property name,
receiver – if the target property is a getter, then receiver is the object that’s going to be used as this in its call. Usually that’s the proxy object itself (or an object that inherits from it, if we inherit from proxy). Right now we don’t need this argument, so it will be explained in more detail later.
Let’s use get to implement default values for an object.
We’ll make a numeric array that returns 0 for nonexistent values.
Usually when one tries to get a non-existing array item, they get undefined, but we’ll wrap a regular array into the proxy that traps reading and returns 0 if there’s no such property:
let numbers = [0, 1, 2];
numbers = new Proxy(numbers, {
get(target, prop) {
if (prop in target) {
return target[prop];} else {
return 0; // default value}}});
alert( numbers[1]); // 1
alert( numbers[123]); // 0 (no such item)
As we can see, it’s quite easy to do with a get trap.
We can use Proxy to implement any logic for “default” values.
Imagine we have a dictionary, with phrases and their translations:
let dictionary = {
‘Hello’: ‘Hola’,
‘Bye’: ‘Adiós’};
alert( dictionary[‘Hello’]); // Hola
alert( dictionary[‘Welcome’]); // undefined
Right now, if there’s no phrase, reading from dictionary returns undefined. But in practice, leaving a phrase untranslated is usually better than undefined. So let’s make it return an untranslated phrase in that case instead of undefined.
To achieve that, we’ll wrap dictionary in a proxy that intercepts reading operations:
dictionary = new Proxy(dictionary, {
get(target, phrase) { // intercept reading a property from dictionary
if (phrase in target) { // if we have it in the dictionary
return target[phrase]; // return the translation} else {
// otherwise, return the non-translated phrase
return phrase;}}});
// Look up arbitrary phrases in the dictionary!
// At worst, they’re not translated.
alert( dictionary[‘Welcome to Proxy’]); // Welcome to Proxy (no translation)
Please note:
Please note how the proxy overwrites the variable:
dictionary = new Proxy(dictionary,… );
The proxy should totally replace the target object everywhere. No one should ever reference the target object after it got proxied. Otherwise it’s easy to mess up.
Validation with “set” trapLet’s say we want an array exclusively for numbers. If a value of another type is added, there should be an error.
The set trap triggers when a property is written.
set(target, property, value, receiver):
value – property value,
receiver – similar to get trap, matters only for setter properties.
The set trap should return true if setting is successful, and false otherwise (triggers TypeError).
Let’s use it to validate new values:
let numbers = [];
numbers = new Proxy(numbers, { // (*)
set(target, prop, val) { // to intercept property writing
if (typeof val == ‘number’) {
target[prop] = val;
return true;} else {
return false;}}});
(1); // added successfully
(2); // added successfully
alert(“Length is: ” +); // 2
(“test”); // TypeError (‘set’ on proxy returned false)
alert(“This line is never reached (error in the line above)”);
Please note: the built-in functionality of arrays is still working! Values are added by push. The length property auto-increases when values are added. Our proxy doesn’t break anything.
We don’t have to override value-adding array methods like push and unshift, and so on, to add checks in there, because internally they use the [[Set]] operation that’s intercepted by the proxy.
So the code is clean and concise.
Don’t forget to return true
As said above, there are invariants to be held.
For set, it must return true for a successful write.
If we forget to do it or return any falsy value, the operation triggers TypeError.
Iteration with “ownKeys” and “getOwnPropertyDescriptor”, loop and most other methods that iterate over object properties use [[OwnPropertyKeys]] internal method (intercepted by ownKeys trap) to get a list of properties.
Such methods differ in details:
tOwnPropertyNames(obj) returns non-symbol keys.
tOwnPropertySymbols(obj) returns symbol keys.
() returns non-symbol keys/values with enumerable flag (property flags were explained in the article Property flags and descriptors).
loops over non-symbol keys with enumerable flag, and also prototype keys.
…But all of them start with that list.
In the example below we use ownKeys trap to make loop over user, and also and, to skip properties starting with an underscore _:
let user = {
name: “John”,
age: 30,
_password: “***”};
user = new Proxy(user, {
ownKeys(target) {
return (target)(key =>! artsWith(‘_’));}});
// “ownKeys” filters out _password
for(let key in user) alert(key); // name, then: age
// same effect on these methods:
alert( (user)); // name, age
alert( (user)); // John, 30
So far, it works.
Although, if we return a key that doesn’t exist in the object, won’t list it:
let user = {};
return [‘a’, ‘b’, ‘c’];}});
alert( (user)); //
Why? The reason is simple: returns only properties with the enumerable flag. To check for it, it calls the internal method [[GetOwnProperty]] for every property to get its descriptor. And here, as there’s no property, its descriptor is empty, no enumerable flag, so it’s skipped.
For to return a property, we need it to either exist in the object, with the enumerable flag, or we can intercept calls to [[GetOwnProperty]] (the trap getOwnPropertyDescriptor does it), and return a descriptor with enumerable: true.
Here’s an example of that:
ownKeys(target) { // called once to get a list of properties
return [‘a’, ‘b’, ‘c’];},
getOwnPropertyDescriptor(target, prop) { // called for every property
return {
enumerable: true,
configurable: true
/*.. flags, probable “value:… ” */};}});
alert( (user)); // a, b, c
Let’s note once again: we only need to intercept [[GetOwnProperty]] if the property is absent in the object.
Protected properties with “deleteProperty” and other trapsThere’s a widespread convention that properties and methods prefixed by an underscore _ are internal. They shouldn’t be accessed from outside the object.
Technically that’s possible though:
_password: “secret”};
alert(user. _password); // secret
Let’s use proxies to prevent any access to properties starting with _.
We’ll need the traps:
get to throw an error when reading such property,
set to throw an error when writing,
deleteProperty to throw an error when deleting,
ownKeys to exclude properties starting with _ from and methods like
Here’s the code:
if (artsWith(‘_’)) {
throw new Error(“Access denied”);}
let value = target[prop];
return (typeof value === ‘function’)? (target): value; // (*)},
throw new Error(“Access denied”);} else {
return true;}},
deleteProperty(target, prop) { // to intercept property deletion
delete target[prop];
ownKeys(target) { // to intercept property list
// “get” doesn’t allow to read _password
try {
alert(user. _password); // Error: Access denied} catch(e) { alert(ssage);}
// “set” doesn’t allow to write _password
user. _password = “test”; // Error: Access denied} catch(e) { alert(ssage);}
// “deleteProperty” doesn’t allow to delete _password
delete user. _password; // Error: Access denied} catch(e) { alert(ssage);}
for(let key in user) alert(key); // name
Please note the important detail in the get trap, in the line (*):
//…
return (typeof value === ‘function’)? (target): value; // (*)}
Why do we need a function to call (target)?
The reason is that object methods, such as eckPassword(), must be able to access _password:
user = {
checkPassword(value) {
// object method must be able to read _password
return value === this. _password;}}
A call to eckPassword() gets proxied user as this (the object before dot becomes this), so when it tries to access this. _password, the get trap activates (it triggers on any property read) and throws an error.
So we bind the context of object methods to the original object, target, in the line (*). Then their future calls will use target as this, without any traps.
That solution usually works, but isn’t ideal, as a method may pass the unproxied object somewhere else, and then we’ll get messed up: where’s the original object, and where’s the proxied one?
Besides, an object may be proxied multiple times (multiple proxies may add different “tweaks” to the object), and if we pass an unwrapped object to a method, there may be unexpected consequences.
So, such a proxy shouldn’t be used everywhere.
Private properties of a class
Modern JavaScript engines natively support private properties in classes, prefixed with #. They are described in the article Private and protected properties and methods. No proxies required.
Such properties have their own issues though. In particular, they are not inherited.
“In range” with “has” trapLet’s see more examples.
We have a range object:
let range = {
start: 1,
end: 10};
We’d like to use the in operator to check that a number is in range.
The has trap intercepts in calls.
has(target, property)
target – is the target object, passed as the first argument to new Proxy,
property – property name
Here’s the demo:
range = new Proxy(range, {
has(target, prop) {
return prop >= && prop <=;}}); alert(5 in range); // true alert(50 in range); // false Nice syntactic sugar, isn’t it? And very simple to implement. Wrapping functions: "apply"We can wrap a proxy around a function as well. The apply(target, thisArg, args) trap handles calling a proxy as function: target is the target object (function is an object in JavaScript), thisArg is the value of this. args is a list of arguments. For example, let’s recall delay(f, ms) decorator, that we did in the article Decorators and forwarding, call/apply. In that article we did it without proxies. A call to delay(f, ms) returned a function that forwards all calls to f after ms milliseconds. Here’s the previous, function-based implementation: function delay(f, ms) { // return a wrapper that passes the call to f after the timeout return function() { // (*) setTimeout(() => (this, arguments), ms);};}
function sayHi(user) {
alert(`Hello, ${user}! `);}
// after this wrapping, calls to sayHi will be delayed for 3 seconds
sayHi = delay(sayHi, 3000);
sayHi(“John”); // Hello, John! (after 3 seconds)
As we’ve seen already, that mostly works. The wrapper function (*) performs the call after the timeout.
But a wrapper function does not forward property read/write operations or anything else. After the wrapping, the access is lost to properties of the original functions, such as name, length and others:
return function() {
alert(); // 1 (function length is the arguments count in its declaration)
alert(); // 0 (in the wrapper declaration, there are zero arguments)
Proxy is much more powerful, as it forwards everything to the target object.
Let’s use Proxy instead of a wrapping function:
return new Proxy(f, {
apply(target, thisArg, args) {
setTimeout(() => (thisArg, args), ms);}});}
alert(); // 1 (*) proxy forwards “get length” operation to the target
The result is the same, but now not only calls, but all operations on the proxy are forwarded to the original function. So is returned correctly after the wrapping in the line (*).
We’ve got a “richer” wrapper.
Other traps exist: the full list is in the beginning of this article. Their usage pattern is similar to the above.
ReflectReflect is a built-in object that simplifies creation of Proxy.
It was said previously that internal methods, such as [[Get]], [[Set]] and others are specification-only, they can’t be called directly.
The Reflect object makes that somewhat possible. Its methods are minimal wrappers around the internal methods.
Here are examples of operations and Reflect calls that do the same:
Operation
Reflect call
Internal method
obj[prop]
(obj, prop)
obj[prop] = value
(obj, prop, value)
delete obj[prop]
leteProperty(obj, prop)
new F(value)
nstruct(F, value)

For example:
(user, ‘name’, ‘John’);
alert(); // John
In particular, Reflect allows us to call operators (new, delete…) as functions (nstruct, leteProperty, …). That’s an interesting capability, but here another thing is important.
For every internal method, trappable by Proxy, there’s a corresponding method in Reflect, with the same name and arguments as the Proxy trap.
So we can use Reflect to forward an operation to the original object.
In this example, both traps get and set transparently (as if they didn’t exist) forward reading/writing operations to the object, showing a message:
name: “John”, };
get(target, prop, receiver) {
alert(`GET ${prop}`);
return (target, prop, receiver); // (1)},
set(target, prop, val, receiver) {
alert(`SET ${prop}=${val}`);
return (target, prop, val, receiver); // (2)}});
let name =; // shows “GET name”
= “Pete”; // shows “SET name=Pete”
Here:
reads an object property.
writes an object property and returns true if successful, false otherwise.
That is, everything’s simple: if a trap wants to forward the call to the object, it’s enough to call Reflect. with the same arguments.
In most cases we can do the same without Reflect, for instance, reading a property (target, prop, receiver) can be replaced by target[prop]. There are important nuances though.
Proxying a getterLet’s see an example that demonstrates why is better. And we’ll also see why get/set have the third argument receiver, that we didn’t use before.
We have an object user with _name property and a getter for it.
Here’s a proxy around it:
_name: “Guest”,
get name() {
return this. _name;}};
let userProxy = new Proxy(user, {
return target[prop];}});
alert(); // Guest
The get trap is “transparent” here, it returns the original property, and doesn’t do anything else. That’s enough for our example.
Everything seems to be all right. But let’s make the example a little bit more complex.
After inheriting another object admin from user, we can observe the incorrect behavior:
return target[prop]; // (*) target = user}});
let admin = {
__proto__: userProxy,
_name: “Admin”};
// Expected: Admin
alert(); // outputs: Guest (?!? )
Reading should return “Admin”, not “Guest”!
What’s the matter? Maybe we did something wrong with the inheritance?
But if we remove the proxy, then everything will work as expected.
The problem is actually in the proxy, in the line (*).
When we read, 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 such situations, we need receiver, the third argument of get trap. It keeps the correct this to be passed to a getter. In our case that’s admin.
How to pass the context for a getter? For a regular function we could use call/apply, but that’s a getter, it’s not “called”, just accessed.
can do that. Everything will work right if we use it.
Here’s the corrected variant:
get(target, prop, receiver) { // receiver = admin
return (target, prop, receiver); // (*)}});
alert(); // Admin
Now receiver that keeps a reference to the correct this (that is admin), is passed to the getter using in the line (*).
We can rewrite the trap even shorter:
return (guments);}
Reflect calls are named exactly the same way as traps and accept the same arguments. They were specifically designed this way.
So, return Reflect… provides a safe no-brainer to forward the operation and make sure we don’t forget anything related to that.
Proxy limitationsProxies provide a unique way to alter or tweak the behavior of the existing objects at the lowest level. Still, it’s not perfect. There are limitations.
Built-in objects: Internal slotsMany built-in objects, for example Map, Set, Date, Promise and others make use of so-called “internal slots”.
These are like properties, but reserved for internal, specification-only purposes. For instance, Map stores items in the internal slot [[MapData]]. Built-in methods access them directly, not via [[Get]]/[[Set]] internal methods. So Proxy can’t intercept that.
Why care? They’re internal anyway!
Well, here’s the issue. After a built-in object like that gets proxied, the proxy doesn’t have these internal slots, so built-in methods will fail.
let map = new Map();
let proxy = new Proxy(map, {});
(‘test’, 1); // Error
Internally, a Map stores all data in its [[MapData]] internal slot. The proxy doesn’t have such a slot. The built-in method method tries to access the internal property this. [[MapData]], but because this=proxy, can’t find it in proxy and just fails.
Fortunately, there’s a way to fix it:
let proxy = new Proxy(map, {
let value = (guments);
return typeof value == ‘function’? (target): value;}});
(‘test’, 1);
alert((‘test’)); // 1 (works! )
Now it works fine, because get trap binds function properties, such as, to the target object (map) itself.
Unlike the previous example, the value of this inside (… ) will be not proxy, but the original map. So when the internal implementation of set tries to access this. [[MapData]] internal slot, it succeeds.
Array has no internal slots
A notable exception: built-in Array doesn’t use internal slots. That’s for historical reasons, as it appeared so long ago.
So there’s no such problem when proxying an array.
Private fieldsA similar thing happens with private class fields.
For example, getName() method accesses the private #name property and breaks after proxying:
class User {
#name = “Guest”;
getName() {
return this. #name;}}
let user = new User();
user = new Proxy(user, {});
alert(tName()); // Error
The reason is that private fields are implemented using internal slots. JavaScript does not use [[Get]]/[[Set]] when accessing them.
In the call getName() the value of this is the proxied user, and it doesn’t have the slot with private fields.
Once again, the solution with binding the method makes it work:
alert(tName()); // Guest
That said, the solution has drawbacks, as explained previously: it exposes the original object to the method, potentially allowing it to be passed further and breaking other proxied functionality.
Proxy! = targetThe proxy and the original object are different objects. That’s natural, right?
So if we use the original object as a key, and then proxy it, then the proxy can’t be found:
let allUsers = new Set();
constructor(name) {
= name;
(this);}}
let user = new User(“John”);
alert((user)); // true
alert((user)); // false
As we can see, after proxying we can’t find user in the set allUsers, because the proxy is a different object.
Proxies can’t intercept a strict equality test ===
Proxies can intercept many operators, such as new (with construct), in (with has), delete (with deleteProperty) and so on.
But there’s no way to intercept a strict equality test for objects. An object is strictly equal to itself only, and no other value.
So all operations and built-in classes that compare objects for equality will differentiate between the object and the proxy. No transparent replacement here.
Revocable proxiesA revocable proxy is a proxy that can be disabled.
Let’s say we have a resource, and would like to close access to it any moment.
What we can do is to wrap it into a revocable proxy, without any traps. Such a proxy will forward operations to object, and we can disable it at any moment.
The syntax is:
let {proxy, revoke} = vocable(target, handler)
The call returns an object with the proxy and revoke function to disable it.
Here’s an example:
let object = {
data: “Valuable data”};
let {proxy, revoke} = vocable(object, {});
// pass the proxy somewhere instead of object…
alert(); // Valuable data
// later in our code
revoke();
// the proxy isn’t working any more (revoked)
alert(); // Error
A call to revoke() removes all internal references to the target object from the proxy, so they are no longer connected.
Initially, revoke is separate from proxy, so that we can pass proxy around while leaving revoke in the current scope.
We can also bind revoke method to proxy by setting = revoke.
Another option is to create a WeakMap that has proxy as the key and the corresponding revoke as the value, that allows to easily find revoke for a proxy:
let revokes = new WeakMap();
(proxy, revoke);
// mewhere else in our code..
revoke = (proxy);
alert(); // Error (revoked)
We use WeakMap instead of Map here because it won’t block garbage collection. If a proxy object becomes “unreachable” (e. no variable references it any more), WeakMap allows it to be wiped from memory together with its revoke that we won’t need any more.
References
Specification: Proxy.
MDN: Proxy.
SummaryProxy is a wrapper around an object, that forwards operations on it to the object, optionally trapping some of them.
It can wrap any kind of object, including classes and functions.
let proxy = new Proxy(target, {
/* traps */});
…Then we should use proxy everywhere instead of target. A proxy doesn’t have its own properties or methods. It traps an operation if the trap is provided, otherwise forwards it to target object.
We can trap:
Reading (get), writing (set), deleting (deleteProperty) a property (even a non-existing one).
Calling a function (apply trap).
The new operator (construct trap).
Many other operations (the full list is at the beginning of the article and in the docs).
That allows us to create “virtual” properties and methods, implement default values, observable objects, function decorators and so much more.
We can also wrap an object multiple times in different proxies, decorating it with various aspects of functionality.
The Reflect API is designed to complement Proxy. For any Proxy trap, there’s a Reflect call with same arguments. We should use those to forward calls to target objects.
Proxies have some limitations:
Built-in objects have “internal slots”, access to those can’t be proxied. See the workaround above.
The same holds true for private class fields, as they are internally implemented using slots. So proxied method calls must have the target object as this to access them.
Object equality tests === can’t be intercepted.
Performance: benchmarks depend on an engine, but generally accessing a property using a simplest proxy takes a few times longer. In practice that only matters for some “bottleneck” objects though.

Frequently Asked Questions about proxy_new

Leave a Reply

Your email address will not be published. Required fields are marked *