DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 963,274 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Cover image for Real world use cases of object proxies
Jeevan Kishore
Jeevan Kishore

Posted on • Updated on

Real world use cases of object proxies

Standard definition

The Proxy object enables you to create a proxy for another object, which can intercept and redefine fundamental operations for that object.

Thinking

Let's simplify that a little.

"Customise the behaviour of an object, using handlers" -- in a broad sense.

Target and Handlers

Target is the object of concern on which Proxy is applied

Handlers or traps are functions passed as second parameter to Proxy which are responsible to handle actions (such as get, set etc) on the target

new Proxy(target, handler);
Enter fullscreen mode Exit fullscreen mode

Let's look at some examples which will add weight around what the statement intends to convey.

Handle defaults

Consider a script which is to be used to match the winners with prizes they have won and everyone who participated gets a consolation prize.

A cleaner approach to coding it as opposed to a long list of if-else statements or switch is to use Proxy:

const prizeRegistry = {
  karen: "First Prize - BMW X5",
  mark: "Second Prize - Scooty",
  athira: "Third Prize - Bicycle"
};

const prizeHandler = {
  get(obj, prop) {
    return prop in obj ?
      obj[prop] :
      "Consolation prize - Chocolate Bar";
  }
};

const prizeList = new Proxy(prizeRegistry, prizeHandler);

console.log(prizeList.karen);
// expected output: "First Prize - BMW X5"

console.log(prizeList.reena);
// expected output: "Consolation prize - Chocolate Bar"
Enter fullscreen mode Exit fullscreen mode

Input validations

With user inputs being a cog in the wheel to most of the applications today. Validations are to be placed to keep the data clean, ironically often as a result the code responsible gets messed up with time.

const validator = {
  set(obj, prop, value) {
    if (prop === 'weight') {
      if (!Number.isInteger(value)) {
        throw new TypeError('The weight is not an integer');
      }
      if (value > 200) {
        throw new RangeError('The weight seems invalid');
      }
    }

    // The default behavior to store the value
    obj[prop] = value;

    // Indicate success
    return true;
  }
};

const fish = new Proxy({}, validator);

fish.weight = 100;
console.log(fish.weight); 
// expected output: 100
fish.weight = 'small';    // Throws an exception
fish.weight = 300;        // Throws an exception
Enter fullscreen mode Exit fullscreen mode

Finding an array item object by its property

const products = new Proxy([
  { name: 'Firefox', type: 'browser' },
  { name: 'SeaMonkey', type: 'browser' },
  { name: 'Thunderbird', type: 'mailer' }
],
{
  get(obj, prop) {
    // 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 products.length
    if (prop === 'number') {
      return obj.length;
    }

    let result;
    const types = {};

    for (const product of obj) {
      if (product.name === prop) {
        result = product;
      }
      if (types[product.type]) {
        types[product.type].push(product);
      } else {
        types[product.type] = [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 Object.keys(types);
    }

    return undefined;
  }
});

console.log(products[0]);          // { name: 'Firefox', type: 'browser' }
console.log(products['Firefox']);  // { name: 'Firefox', type: 'browser' }
console.log(products['Chrome']);   // undefined
console.log(products.browser);     // [{ name: 'Firefox', type: 'browser' }, { name: 'SeaMonkey', type: 'browser' }]
console.log(products.types);       // ['browser', 'mailer']
console.log(products.number);      // 3

Enter fullscreen mode Exit fullscreen mode

The above snippet is from mozilla documentation, showing how an object in an array of objects can be optimally found using proxy

There are a number of other real-world usecase(s) that could leverage the awesomeness of Proxy to better maintain and help keep the code cleaner.

P.S These are the few i use day in and out, there are few more such as DOM manupulations, Property forwarding. You can check em out here.

Hope this helps. Cheers 🍻

Top comments (7)

Collapse
 
perssondennis profile image
Dennis Persson

Great post. I hoped to learn about fish when looking at the header image though πŸ˜„πŸŽ£

Collapse
 
webreflection profile image
Andrea Giammarchi

more use cases in proxy-pants (and counting, as I've been using Proxy for way too many things recently)

Collapse
 
devangtomar profile image
Devang Tomar

That was a nice read! Liked, bookmarked and followed, keep the good work! πŸ™Œ

Collapse
 
yuridevat profile image
Julia πŸ‘©πŸ»β€πŸ’»

Thanks for sharing some use cases for proxy. This pattern always confuses me πŸ˜…

Collapse
 
naubit profile image
Al - Naubit

Thanks, it was a good read, bookmarked, and followed!

Collapse
 
mailuon39703122 profile image
Mai Lượng

good, thankyou

Collapse
 
silverium profile image
Info Comment hidden by post author - thread only accessible via permalink
Soldeplata Saketos

looks like the get function in
"Finding an array item object by its property"
will run every time we access to the proxy properties, looping through all the elements of the proxied object. That does not look "optimal" to me

Some comments have been hidden by the post's author - find out more

Need a better mental model for async/await?

Check out this classic DEV post on the subject.

β­οΈπŸŽ€ JavaScript Visualized: Promises & Async/Await

async await