Traps and proxy
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.
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
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.
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