The simplest example of a functioning Proxy is one with a single trap, in this case, a get trap that always returns “42”.

The result is an object that will return “42” for any property access operation. That means target.x , target['x'] , Reflect.get(target, 'x') , etc.

However, Proxy traps are certainly not limited to get operations. It is only one of more than a dozen different traps:

Proxy Use Cases

It may not be immediately apparent how such a simple pattern could be so widely used, but hopefully, after a few examples, it will become more clear.

Default/ “Zero Values”

In GoLang, there is the concept of zero values which are type-specific, implicit default struct values. The idea is to provide type-safe default primitives values, or in Gopher-speak, “give your structs a useful zero value!”

Though different creation patterns enable similar functionality, Javascript had no way to wrap an object with implicit initial values. The default value for an unset property in Javascript is undefined . That is, until Proxy.

This three-line function wraps a target object. If the property is set, it returns the property value (pass-through). Otherwise, it returns a default “zero value.” Technically, this approach isn’t implicit either but it could be if we extended withZeroValue to support type-specific (rather than parameterized) zero values for Boolean ( false ), Number ( 0 ), String ( “” ), Object ( {} ), Array ( [] ), etc.

One place where this functionality might be useful is a coordinate system. Plotting libraries may automatically support 2D and 3D rendering based on the shape of the data. Rather than create two separate models, it might make sense to always include z defaulted to zero rather than undefined .

Negative Array Indices

Getting the last element in an Array in Javascript is verbose, repetitive, and prone to off-by-one errors. That’s why there is a TC39 proposal that defines a convenience property, Array.lastItem , to get and set the last element.

Other languages like Python and Ruby make access to terminal elements easier with negative array indices. For example, the last element can be accessed simply with arr[-1] instead of arr[arr.length-1] .

With Proxy, negative indices can also be used in Javascript.

One important note is that traps including handler.get stringify all properties. For array access, we need to coerce property names into Numbers which can be done concisely with the unary plus operator.

Now [-1] accesses the last element, [-2] the second to last, and so on.

There is even an npm package, negative-array , that encapsulates this functionality more completely.

Hiding Properties

Javascript has notoriously lacked private properties. Symbol was originally introduced to enable private properties, but later watered down with reflective methods like Object.getOwnPropertySymbols that made them publicly discoverable.

The longstanding convention has been to name private properties with a leading underscore, effectively marking them “do not touch.” Proxy offers a slightly better approach to masking such properties.

The hide function wraps a target object and makes properties that are prefixed with an underscore inaccessible from the in operator and from methods like Object.getOwnPropertyNames.

A more complete implementation would also include traps like deleteProperty and defineProperty. Apart from closures, this is probably the approach that is closest to truly-private properties as they are inaccessible from enumeration, cloning, access, or modification.

They are, however, visible in the development console. Only closures are exempt from this fate.

Caching

There are two hard problems in computer science: cache invalidation, naming things, and off-by-one errors.

It is not uncommon to face difficulties synchronizing state between the client and server. Data can change over time, and it can be difficult to know exactly where to place the logic of when to re-sync.

Proxy enables a new approach: wrap objects to invalidate (and resync) properties as necessary. All attempts to access a property first check against a cacheing strategy that decides to returns what’s currently in memory, or to take some other action.

This function is oversimplified: it renders all properties on an object inaccessible after a certain period of time. However, it would not be difficult to extend this approach to set time-to-live (TTL) on a per-property basis and to update it after a a certain duration or number of accesses.

This example simply renders the bank account balance inaccessible after 10 second. For more in-depth, real-world use cases there are several articles on Caching & Logging and Client-Side Caching using Proxy and sessionStorage .

Enums & Read-Only Views

These examples come from Csaba Hellinger’s article on Proxy Use Cases and Mozilla Hacks. The approach is to wrap an object to prevent extension or modification. Although Object.freeze now provides functionality to render an object read-only, it’s possible to extend this approach for better enum objects that throw errors accessing non-existent properties.

Read-Only View

Enum View

Now we can create an Object that throws an exception if you try to access non-exist properties, rather than returning undefined . This makes it easier to catch and address issues early on.

Our enum example is also the first example of proxies on proxies, confirming that a proxy is a valid target object for another proxy. This facilitates code reuse through composition of Proxy functionality.

This approach can be further extended to include “simulated methods” like nameOf that return the property name given an enum value, mimicking the behavior in languages like Javascript.

While other frameworks and language supersets like TypeScript offer an enum type, this solution is unique in that it works with vanilla Javascript without special build tools or transpilers.

Operator Overload

Perhaps the most fascinating Proxy use case syntactically is the ability to overload operators, like the in operator using handler.has.

The in operator is designed to check if a “specified property is in the specified object or its prototype chain.” But it is also the most syntactically-elegant operator to overload. This example defines a continuous range function to compare Numbers against.

Unlike Python, which uses generators to compare against a finite sequence of whole numbers, this approach supports decimal comparison and could be extended to support other numeric ranges–inclusive, exclusive, natural, rational, imaginary, ad infinitum.

Even though this use case does not solve a complex problem, it does provide clean, readable, and reusable code. 🔥

In addition to the in operator, we can also overload delete and new .

Cookies Object

If you have ever had to interface with cookies in Javascript, you have had to deal with document.cookie . It is an unusual API in that the API is a String that reads out all cookies, semi-colon delimited but you use the assignment operator to initialize or overwrite a single cookie.

document.cookie is a String that looks like:

_octo=GH1.2.2591.47507; _ga=GA1.1.62208.4087; has_recent_activity=1

In short, dealing with document.cookie is frustrating and error prone. One approach is the simple cookie framework, which can be adapted to use Proxy.

This function returns an object that acts like any other key-value object, but proxies all changes to document.cookie for persistence.

In 11 lines, we have a better interface for modifying cookies, although additional features like string normalization would be necessary in a production environment.