Engineering

Improving Performance and Reducing Code Complexity with JavaScript Proxy

-By Arjun Nair

JavaScript's Proxy is a powerful feature that allows developers to create custom behaviors for basic object operations like property access, assignment, and deletion. Developers can intercept these operations and execute custom code before or after they occur by using a Proxy. This is useful in a variety of situations, including data validation, access control, and performance optimization.

This blog will go over how to use the JavaScript Proxy feature, including its syntax, capabilities, and common use cases.

 

Proxy Syntax

The syntax for creating a Proxy object is simple. Here's an illustration:

 

const proxy = new Proxy(target, handler);

 

The target parameter is the object that the Proxy is wrapping, and the handler parameter is an object that defines the Proxy's custom behaviors. One or more trap methods, which are functions that intercept basic object operations, must be present in the handler object. The get trap, for example, prevents property access, the set trap prevents property assignment, and the deleteProperty trap prevents property deletion.

 

Proxy Capabilities

The Proxy feature of JavaScript is extremely versatile and can be used to implement a wide range of custom behaviours. Let us look at some use cases:

 

Data Validation

Data validation is a common application for Proxy. Developers can ensure that only valid data is stored in an object by intercepting property assignments and validating the data. Here's an illustration:

 

const person =new Proxy({}, {

 set(target, prop, value) {

   if (prop === 'age' && typeof value!== 'number') {

     throw new TypeError('Age must be a number');

   }

   target[prop] = value;

   return true;

 }

});

 

person.age =25; // ok

person.age ='twenty-five'; // throws TypeError

 

 

Access Control

Access control is another application for Proxy. Developers can control who has access to certain properties of an object by intercepting property access and checking the user's permissions. Here's an illustration:

 

const user = {name: 'Alice', password: 'secret' };

 

constsecureUser = new Proxy(user, {

 get(target, prop, receiver) {

   if (prop === 'password') {

     throw new Error('Access denied');

   }

   return Reflect.get(target, prop, receiver);

 }

});

 

console.log(secureUser.name);// 'Alice'

console.log(secureUser.password);// throws Error

 

 

Performance Optimization

The third application of Proxy is performance optimization. Developers can avoid redundant computations and speed up their code by intercepting property access and caching the results. Here's an illustration:

 

functionexpensiveCalculation() {

 // some expensive computation

 return Math.random();

}

 

constcachedResult = new Proxy({}, {

 get(target, prop, receiver) {

   if (!(prop in target)) {

     target[prop] = expensiveCalculation();

   }

   return Reflect.get(target, prop, receiver);

 }

});

 

console.log(cachedResult.foo);// some random number

console.log(cachedResult.foo);// the same random number as before

 

 

Logging of warnings on the platform

We needed a project-wide feature that logs a warning to our logging platforms when query selectors return null, indicating that the element does not exist on the DOM. We created proxies for the four query selectors we used in our code:

·      querySelectorAll

·      querySelector

·      getElementById

·      getElementsByClassName

 

Here’s an illustration of the code:

 

consterrorHandler = function () {

   return {

       get: function (obj, prop) {

           return function (identifier) {

               let output =document[prop](identifier);

               if (output == null) {

                   // custom code to log our ogging platform

               }

               return output;

           }

       }

   }

}

 

functiondocumentProxy() {

   return new Proxy({

       querySelectorAll:document.querySelectorAll,

       querySelector: document.querySelector,

       getElementById:document.getElementById,

       getElementsByClassName:document.getElementsByClassName

   }, errorHandler())

}

 

Usage

const document= documentProxy();

const node =document.querySelector(selector);

You may also like