Practical use cases for JavaScript ES6 proxies
Tags: javascript
Published: 2020-06-02 15:35:49

---
title: Practical use cases for JavaScript ES6 proxies
published: true
date: 2020-05-15 16:00:11 UTC
tags: javascript
canonical_url: https://blog.logrocket.com/practical-use-cases-for-javascript-es6-proxies/
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/x3op719ch761v9jzcmv6.png
---
**Written by [Eslam Hefnawy](https://blog.logrocket.com/author/eslamhefnawy/)**✏️

Metaprogramming is a powerful technique that enables you to write programs that can create other programs. ES6 made it easier to utilize metaprogramming in JavaScript with the help of proxies and many similar features. [ES6 proxies](https://dev.to/bnevilleoneill/3-ways-to-use-es6-proxies-to-enhance-your-objects-506p) facilitate the redefinition of fundamental operations in an object, opening the door for a wide variety of possibilities.

In this guide, we’ll show you how to apply ES6 proxies in practical situations.

## Prerequisites and outcomes

This tutorial is primarily aimed at developers who have experience with JavaScript and are at least familiar with the idea of ES6 proxies. If you have a firm understanding of proxies as a design pattern, that knowledge should translate.

After reading this guide, you should be able to:

- Understand what an ES6 Proxy is, how to implement one, and when to use it
- Use ES6 Proxies for access control, caching, and data binding

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

## Anatomy of an ES6 proxy: Target, handler, and trap

Fundamentally, a proxy is something or someone that becomes something else’s substitute, so that whatever it is, it has to go through the substitute to reach the real deal. An ES6 proxy works the same way.

To effectively implement and use an ES6 proxy, you must understand three key terms:

1. **Target** — The real deal that the proxy is substituting, the target is what stands behind the proxy. This can be any object
2. **Handler**— An object that contains the logic of all the proxy’s traps
3. **Trap** — Similar to traps in operating systems, [traps](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) in this context are methods that provide access to the object in a certain way

Putting all this together, below is the simplest implementation in which you can return something different if a given property doesn’t exist in an object using an ES6 proxy.

```jsx
const target = {
    someProp: 1
}

const handler = {
    get: function(target, key) {
        return key in target ? 
        target[key] : 
        'Doesn't exist!';
    }
}

const proxy = new Proxy(target, handler);
console.log(proxy.someProp) // 1
console.log(proxy.someOtherProp) // Doesn't exist!
```

An ES6 proxy is a powerful feature that facilitates the [virtualization](https://www.redhat.com/en/topics/virtualization/what-is-virtualization) of objects in JavaScript.

## Data binding: Syncing multiple objects

Data binding is often difficult to achieve due to its complexity. The application of ES6 proxies to achieve two-way data binding can be seen among model-view-controller libraries in JavaScript, where an object is modified when the DOM undergoes a change.

To put it simply, data binding is a technique that binds multiple data sources together to synchronize them.

Suppose that there is an `<input>` with the id of `username`.

```jsx
<input type="text" id="username" /> 
```

Let’s say you want to keep the value of this input in sync with a property of an object.

```jsx
const inputState = {
    id: 'username',
    value: ''
}
```

It’s quite easy to modify the `inputState` when the value of the `input` changes by listening to the `change` event of the input and then updating `inputState`‘s value. However, the reverse — updating the `input` when the `inputState` is modified — is quite difficult.

An ES6 proxy can help in such a situation.

```jsx
const input = document.querySelector('#username')
const handler = {
    set: function(target, key, value) {
        if (target.id && key === 'username') {
            target[key] = value;
            document.querySelector(`#${target.id}`)
            .value = value;
            return true
        }
        return false
    }
}

const proxy = new Proxy(inputState, handler)
proxy.value = 'John Doe'
console.log(proxy.value, input.value) 
// 'John Doe' will be printed for both
```

This way, when the `inputState` changes, the `input` will reflect the change that has been made. Combined with listening to the `change` event, this will produce a simple two-way data binding of the `input` and `inputState`.

While this is a valid use case, it’s generally not encouraged. More on that later.

## Caching: Enhancing code performance

Caching is an ancient concept that allows very complex and large applications to remain relatively performant. Caching is the process of storing certain pieces of data so they can be served much faster when requested. A cache doesn’t store any data permanently. Cache invalidation is the process of ensuring that the cache is fresh. This is a common struggle for developers. As [Phil Karlton](https://quotesondesign.com/phil-karlton/) said, “There are only two hard things in Computer Science: cache invalidation and naming things.”

ES6 proxies make caching easier. If you want to check whether something exists in an object, for example, it would first check the cache and return the data or do something else to obtain that data if it doesn’t exist.

Let’s say you need to make a lot of API calls to obtain a specific piece of information and do something with it.

```jsx
const getScoreboad = (player) => {
    fetch('some-api-url')
    .then((scoreboard) => {
        // do something with scoreboard
    })
}
```

This would mean that whenever the scoreboard of a player is required, a new call has to be made. Instead, you could cache the scoreboard when it is first requested, and subsequent requests tcould be taken from the cache instead.

```jsx
const cache = { 
    'John': ['55', '99']
}
const handler = { 
    get: function(target, player) {
        if(target[player] {
            return target[player]
        } else {
            fetch('some-api-url')
            .then((scoreboard => {
                target[player] = scoreboard
                return scoreboard
            })
        }
    }
}
const proxy = new Proxy(cache, handler)
// access cache and do something with scoreboard
```

This way, an API call will only be made if the cache doesn’t contain the player’s scoreboard.

## Access control: Controlling what goes in and out of objects

The simplest use case is access control. Most of what the ES6 proxy is known for falls under access control. The scenario we walked through to show how to implement proxies is an example of access control.

Let’s explore a few practical applications of access control using an E6 proxy.

### 1. Validation

One of the most intuitive use cases for ES6 proxies is validating what comes inside your object to ensure that the data in your object is as accurate as possible. For example, if you want to enforce a maximum number of characters for a product description, you could do so like this:

```jsx
const productDescs = {}
const handler = {
    set: function(target, key, value) {
        if(value.length > 150) {
            value = value.substring(0, 150)
        }
        target[key] = value
    }
}
const proxy = new Proxy(productDescs, handler)
```

Now, even if you add a description that’s longer than 150 characters, it’ll be cut short and added.

### 2. Providing a read-only view of an object

There may come a time when you want to ensure that an object is not modified in any way and can only be used for reading purposes. JavaScript provides `Object.freeze()` to do this, but the behavior is more customizable when using a proxy.

```jsx
const importantData = {
    name: 'John Doe',
    age: 42
}

const handler = {
    set: 'Read-Only',
    defineProperty: 'Read-Only',
    deleteProperty: 'Read-Only',
    preventExtensions: 'Read-Only',
    setPrototypeOf: 'Read-Only'
}

const proxy = new Proxy(importantData, handler)
```

Now when you try to mutate the object in any way, you’ll only receive a string saying `Read Only`. Otherwise, you could throw an error to indicate that the object is read-only.

### 3. Private properties

Javascript doesn’t have private properties per se, except for closures. When the `Symbol` data type was introduced, it was used to mimic private properties. But it fell by the wayside with the introduction of the `Object.getOwnPropertySymbols` method. ES6 proxies aren’t a perfect solution, but they do the job in a pinch. A common convention is to identify a private property by prepending an underscore before its name. This convention enables us to use ES6 proxies.

```jsx
const object = {
    _privateProp: 42
}

const handler = {
    has: function(target, key) {
        return !(key.startsWith('_') && key in target)
    },
    get: function(target, key, receiver) {
        return key in receiver ? target[key] : undefined
    }
}

const proxy = new Proxy(object, handler)
proxy._privateProp // undefined
```

Adding the `ownKeys` and `deleteProperty` will bring this implementation closer to being a truly private property. Then again, you can still view a proxy object’s in the developer console. If your use case aligns with the above implementation, it’s still applicable.

## Why and when to use proxies

ES6 proxies are not ideal for performance-intensive tasks. That’s why it’s crucial to perform the necessary testing. A proxy can be used wherever an object is expected, and the complex functionality that proxies provide within a few lines of code makes it an ideal feature for metaprogramming. Proxies are typically used alongside another metaprogramming feature known as [Reflect](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect).

## Summary

Hopefully, this guide has helped you understand why ES6 proxies are such a great tool, especially for metaprogramming. You should now know:

- What an ES6 proxy is
- How and when to implement a proxy
- How to use ES6 proxies to perform access control, data binding, and caching
- That ES6 proxies are not ideal for performance-intensive tasks

To learn more, check out the following resources.

- [MDN documentation on Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)
- [MDN documentation on Reflect](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect)
- [Metaprogramming with ES6 proxies by 2ality](https://2ality.com/2014/12/es6-proxies.html)

* * *
## [LogRocket](https://logrocket.com/signup/): Debug JavaScript errors easier by understanding the context 

Debugging code is always a tedious task. But the more you understand your errors the easier it is to fix them. 

[LogRocket](https://logrocket.com/signup/) allows you to understand these errors in new and unique ways. Our frontend monitoring solution tracks user engagement with your JavaScript frontends to give you the ability to find out exavtly what the user did that led to an error. 

![](https://i1.wp.com/blog.logrocket.com/wp-content/uploads/2020/06/reproduce-javascript-errors.gif?ssl=1)

LogRocket records console logs, page load times, stacktraces, slow network requests/responses with headers + bodies, browser metadata, and custom logs. Understanding the impact of your JavaScript code will never be easier. 

[Try it for free.](https://logrocket.com/signup/) 
* * *

The post [Practical use cases for JavaScript ES6 proxies](https://blog.logrocket.com/practical-use-cases-for-javascript-es6-proxies/) appeared first on [LogRocket Blog](https://blog.logrocket.com).