Using Proxy in Javascript

Proxy was introduced in JavaScript as part of the ECMAScript 6 specification. It is an object that wraps another object capable of defining and monitoring the operations being carried out and performed by the object being wrapped. The wrapped object is known as the target object.

Traps and proxy

To understand Javascript Proxy, you need to first know what traps are. Proxies make use of traps for detecting calls or invocation of internal methods of an object, and to intercept these calls, like setting, and getting object properties.

In the above code snippet, the [[SET]] internal method of the object would be invoked by the javascript engine because we are giving assigning a value to our hero object ‘realname’ property. When implemented, Traps would be executed before the internal [[SET]] method is called (before our hero name is set to “black panther”). Traps are triggered once an operation is to be carried on our target object. Implementations of traps are defined in a proxy handler.

Creating a proxy.

the *new* keyword is used to create an instance of the proxy object, the proxy object takes in two parameters:

  • Target: Target is the original object we want to Proxy so operations on this object will go through the handlers.
  • Handlers: It’s an object that defines the traps for intercepting operations on the target and also for enhancing’s intercepted operation.

In the above code, we passed the task object which is the target object, and myhandler to the proxy instance. The handler doesn’t do anything yet because there are no specified traps in it, so the proxyTask complete status will be set to true as if the proxy was never there. If an internal method that is not handled by the handler is invoked, the method is invoked on the proxied object(target) as if it was never proxied.

Let’s Trap some things

Our code doesn’t do anything useful yet, so let’s create traps for our proxy.

So we created a “get” trap, which receives our target object as a parameter. If you run the above code, instead of seeing “john” we see in our console “an attempt to access firstname was made”, this is because of the get handler method in our handler, you see that it intercepted the [[GET]] Internal method. Here is a curated list of available traps.

Note: All handler methods or trap takes the target as a parameter.

More traps

A proxy can be used for various things, like logging, security, profiling and new design patterns, etc. So let’s say we are building a task project and we want to log to the console, once a task is completed.

let me explain what the code is trying to achieve, we create a basic task object with a unique id, title, and complete property, the complete property is false which means that the task hasn’t been completed, changing it to true means we have completed the task, so we created a set trap in our handler, it first check if the property that wants to be set or changed is the complete property of task, and then checks if the value it is being assigned to complete is true, that is to know if the task has been completed, then we use the Reflect API to grab our target id and title property, and log to the console that the task has been completed, more like a notification method, we continue with the operation by using the Reflect API set method to set the value.

Securing access with proxy

Like I earlier stated, javascript Proxy API can be used for security purposes. In the code below we created an object that contains a secret and we don’t want this secret to be accessed externally.

The code throws an error whenever we try to access the secret property. The code snippet above should not be used for a project that could go into production, I only did this to show an example. you can also implement the delete trap just like the same way the code implemented the `get` trap.

prototype and proxy(psquare)

in the above code, sharedProxy is a proxy with an empty target object. To extend it’s `get trap` functionality to a task, we made it the prototype of a task, task inherits the get trap of sharedProxy and would act the same as any other proxied object created using the Proxy constructor.

Calling functions with proxy

What if there is a need to intercept a call to a function? The apply trap handler can help us intercept function calls.

Say we were to make a racing game, and in this game, every player is an athlete and the athletes can only run as far as their strength can carry them. Assuming 50 is the minimum(threshold) amount of energy one athlete needs to have for it to be able to run and that for every 2 miles the athlete covers, the athlete loses one unit of its energy. Therefore, when their energy drops to 49 or less than 50, the athlete will no longer be able to continue the race. I know!! not the kind of game you want to ever play, too predictable! (I couldn’t come up with anything else). To model the athlete in our code, we have two variables,

  • energy: holds the current energy level of the athlete
  • distance: just to simulate a real-life movement

our function run is created, `run` is just a function that decrements our energy level anytime it is called, run first checks if the distance covered by the athlete is a multiple of 2(that is for every two miles covered), then decrements our athlete’s energy. To prevent run from executing once energy is below 50, a new proxy called athlete is created, the proxy can intercept a call to run depending on the athlete energy level, if the athlete energy is less than 50, the athlete becomes too weak to run and if it is greater than 50 the athlete keeps running. so every call to our target function run will be trapped by the proxy apply traps and checks if the athlete’s energy has gone below the threshold, to know whether to execute the function or not.

Revoking Proxy

When a proxy is created then passed on to other objects, it will be there forever. Proxies that can be removed are known as revocable proxies and they are created with `Proxy.revocable` method. It’s a static method of the Proxy object.

Note the absence of new. This is because revocable is a static method of Proxy. When Proxy.revocable is called or used to create a proxy, it returns an object which contains our proxy and a function revoke to revoke the proxy. so let’s revoke a proxy 😊.

what the code above does is to create a revocable proxy with `task` being the proxy target object, then grabs the proxy and the revoke function with destructuring from the object returned from Proxy.revocable, then it prints the title of the task to the console, u see that the title gets printed because our proxy is still intact, To revoke our proxy, we invoke or call the revoke function we got from Proxy.revocable. to confirm that our proxy has been revoked, a second attempt to print the title to the console is made, and we get an error thrown, the error is thrown because our proxy has been successfully revoked.

Limitations of proxies

Not all object usage can be trapped,

  • Object used in comparison can’t be trapped (== and ===)
  • Applying operators on an object or coercing object type can’t be trapped(Number(object), (object +””))
  • Using typeof or instanceof on an object can’t be trapped
  • Some low-level objects like Maps, Set, Date, Promise, Don’t make use of the [[Get/Set]] internal methods but make use of internal slots, which are accessed directly by their built-in methods, so using Get or Set handlers on them will throw errors. This is because the proxy is not the same with the target object, eg. the Map object uses [[MapData]] to store its data, so if you do proxy.get on a proxied Map, it will look for this.[[MapData]]; which is not available on the proxy, but there is a way around this limitation but to keep the article short I won’t go into details you can read about that here https://javascript.info/proxy#proxy-limitations

Conclusion

This is my first article, I hope I was able to introduce you properly to javascript proxy, as time goes on, you guys will see more articles from me. Shout out to everyone who made it possible, I will get better as I write more.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store