Imagine you have a library/SDK that you expose to others to use on their websites. And you want to know what all functions are called by your library users, and how many times. Maybe you want to deprecate the unused ones or may be you want to log the unavailable function calls made on your library. Or say you want to create an internal debugger sort of thing that logs something whenever any function is called in a particular object.
All these scenarios boil down to keeping track of the function calls made on the object. And the most trivial way we can solve this is by patching each function with a little code, like so:
const obj = {
methodOne() {
// logs about `methodOne` being called
// method code here
}
methodTwo() {
// logs about `methodOne` being called
// method code here
}
}
This works but it could be a lot of work when you have a lot of such objects and a lot of methods inside. Patching each function with this extra code can also make the function body less readable and of course, it might bloat the file size a bit.
We have a much better way of dealing with this in JavaScript - Proxy
How Proxies work
As the name suggests, by using a Proxy
you can create an intermediate entity that internally calls our object's properties and methods. Not just that, but you can define at a central place what should happen when these properties/functions get read or written!
Let's see a simple example to understand this:
const target = {
message1: "hello",
message2: "everyone"
};
const handler = {
get: function (target, prop, receiver) {
if (prop === "message2") {
return "world";
}
return Reflect.get(...arguments);
},
};
const proxy = new Proxy(target, handler);
console.log(proxy.message1); // hello
console.log(proxy.message2); // world
This is the simplest example to understand the power of Proxy
. Here we are trying to proxy the target
object. We want to return world
when target.message2
is accessed. For other properties, they should simply work as defined in target
object. This is as simple as defining a handler object with a getter function and telling the proxy to use it against any target object.
Getting Proxy to work for us
Extending from the above example, let's see how we can carry out some use cases:
Logging just method calls
const target = {
message1: "hello",
start: () => { return 'world'; }
};
const handler2 = {
get: function (target, prop, receiver) {
if (typeof target[prop] === "function") {
logFunctionCall(prop);
}
return Reflect.get(...arguments);
},
};
const proxy2 = new Proxy(target, handler2);
console.log(proxy2.message1); // won't log anything
console.log(proxy2.start()); // will log the function call
Logging unavailable method calls and show a message
How nice would it be if you could know what unavailable properties your users access on your library? And when they do so you could show them a message in the developer console about it!
Here is how you can do it easily:
const target = {
message1: "hello",
start: () => { return 'world'; }
};
const handler3 = {
get: function (target, prop, receiver) {
if (typeof target[prop] === "undefined") {
logUnavailablePropertyAccessToServer(prop);
console.log(`property ${prop} is not available in the library. Needs this? Log an issue on github.com/user/yourLib/issues`);
return;
}
return Reflect.get(...arguments);
},
};
const proxy3 = new Proxy(target, handler3);
console.log(proxy3.stop()); // will log function call as unavailable
Note: the above examples just work by defining the get
accessor in the handler. You can do a lot more crazy stuff with the set
accessor! Possibilities of this amazing utility is endless! You can. find more such useful examples on the MDN doc page too - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#examples
Browser Support
The best part is — Proxy
is available in all modern browsers!

Are you getting some crazy ideas of using Proxy
in your app? Let us know in the comments!
Until next time!