A practical guide to Javascript Proxy
Tags: javascript, programming, webdev, software
Published: 2019-05-16 16:19:25

---
title: A practical guide to Javascript Proxy
published: true
description: With Proxy, you get a tiger object disguised as a cat object. Here are about half a dozen examples that I hope will convince you that Proxy provides powerful metaprogramming in Javascript.
tags: Javascript, Programming, Web Development, Software
cover_image: https://thepracticaldev.s3.amazonaws.com/i/xd4sigm450ruom7odd31.jpeg
---

# Introduction to Proxy

With [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy), [you get a tiger object disguised as a cat object](https://blog.bitsrc.io/a-practical-guide-to-es6-proxy-229079c3c2f0). Here are about half a dozen examples that I hope will convince you that Proxy provides powerful metaprogramming in Javascript.

Although it is not as well known as other ES2015 features, Proxy has many uses including [operator overloading](https://hackernoon.com/introducing-javascript-es6-proxies-1327419ab413), [object mocking](https://en.wikipedia.org/wiki/Mock_object), [concise-yet-flexible API creation](https://medium.com/dailyjs/how-to-use-javascript-proxies-for-fun-and-profit-365579d4a9f8), [Object on-change events](https://github.com/sindresorhus/on-change), and even powers the [internal reactivity system behind Vue.js 3](https://medium.com/@gustojs/vuejs-3-and-other-top-news-from-q-a-event-with-core-vue-devs-c9834946ae7b).

> The `Proxy` object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc). – MDN

A Proxy is a placeholder object that “traps” invocations and operations made to its target object which it can then passthrough, no-op, or handle more elegantly. It creates an undetectable barrier around the **target object** that redirects all operations to the **handler object**.

A Proxy is created using the `new Proxy` constructor which accepts two, required arguments: the target object and handler object.

{% gist https://gist.github.com/Tombarr/e73e81228aa4b6a40983422b8b9d6757 %}

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

![](https://thepracticaldev.s3.amazonaws.com/i/pzf1qa0ie1wk7cj65v0l.png)

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](https://tour.golang.org/basics/12) 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](https://en.wikipedia.org/wiki/Creational_pattern) 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.

{% gist https://gist.github.com/Tombarr/907e7d5e6d82263eaf82e642a729cdd9 %}

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.

{% gist https://gist.github.com/Tombarr/5123446f55a718d4576dd0d22671d9a0 %}

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](https://medium.freecodecamp.org/the-first-shall-be-last-with-javascript-arrays-11172fe9c1e0) in Javascript is verbose, repetitive, and prone to off-by-one errors. That’s why there is a [TC39 proposal](https://github.com/keithamus/proposal-array-last) 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](https://rubyquicktips.com/post/996814716/use-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.

{% gist https://gist.github.com/Tombarr/dfb829ad07925baee8513db37ce89045 %}

One important note is that traps including [`handler.get`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Unary_plus_%28%29).

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

{% gist https://gist.github.com/Tombarr/18721367eb38cb8db924168a5cd7f604 %}

There is even an npm package, [negative-array](https://github.com/sindresorhus/negative-array), that encapsulates this functionality more completely.

## Hiding Properties

Javascript has notoriously lacked private properties. `Symbol` was [originally introduced to enable private properties](https://codeburst.io/a-practical-guide-to-es6-symbol-3fc90117c7ac), but later watered down with reflective methods like [`Object.getOwnPropertySymbols`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/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.

{% gist https://gist.github.com/Tombarr/9c4ab1d0efce9b11cf6074efc7a0459e %}

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`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames).

{% gist https://gist.github.com/Tombarr/61d9d2dc40367e07ae2b911e9d2f4d1a %}

A more complete implementation would also include traps like [`deleteProperty`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/deleteProperty) and [`defineProperty`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/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.


## 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.

{% gist https://gist.github.com/Tombarr/0548507afde124b3ae155e5dda0e8c33 %}

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.

{% gist https://gist.github.com/Tombarr/d26b0d9b94de0fe5b735e3ecc7ea5bd0 %}

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](https://medium.com/@niwaa/using-es6-proxy-to-meta-program-in-javascript-implement-caching-logging-etc-577e253b3e05) and [Client-Side Caching using Proxy and `sessionStorage`](https://digitalist.se/blogg/create-client-side-cache-using-es6-proxy).

## Enums & Read-Only Views

These examples come from Csaba Hellinger’s article on [Proxy Use Cases](http://dealwithjs.io/es6-features-10-use-cases-for-proxy/#anameenumaabetterenumtop) and [Mozilla Hacks](https://hacks.mozilla.org/2015/07/es6-in-depth-proxies-and-reflect/). The approach is to wrap an object to prevent extension or modification. Although [`Object.freeze`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/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

{% gist https://gist.github.com/Tombarr/1005b5ac623e7f9ea9562ff2a0f17061 %}

## Enum View

{% gist https://gist.github.com/Tombarr/78251b2b4a38aba9cc13c2445ae3ce98 %}

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.

{% gist https://gist.github.com/Tombarr/870053245afbfdfa8ebf6bd2d8c5417b %}

This approach can be further extended to include “simulated methods” like [`nameOf`](https://github.com/gergob/jsProxy/blob/master/03-enum-nameof.js) 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](https://www.typescriptlang.org/docs/handbook/enums.html), 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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in) using [`handler.has`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/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.

{% gist https://gist.github.com/Tombarr/3e9d7be8d3e32fab791403aaca3b0de7 %}

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.

{% gist https://gist.github.com/Tombarr/7d1940d8b193b07f3a46a864e662e963 %}

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.cookies`](https://developer.mozilla.org/en-US/docs/Web/API/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.cookies` 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.cookies is frustrating and error prone. One approach is the [simple cookie framework](https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie/Simple_document.cookie_framework), which can be adapted to use Proxy.

{% gist https://gist.github.com/Tombarr/c86556b31e15ce1c0f92df57b0049bbc %}

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

{% gist https://gist.github.com/Tombarr/1f97568053ed2832174556a60d8f2149 %}

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

# Fine Print

The devil is in the detail, and Proxy is no exception.

![](https://thepracticaldev.s3.amazonaws.com/i/zmlc93jtj7xwxyyktksu.jpeg)

## Polyfill

At the time of writing (May 2019), there is no complete polyfill for Proxy. There is, however, a partial polyfill for Proxy written by Google that supports the `get`, `set`, `apply`, and `construct` traps and works for IE9+.

## Is it a Proxy?

> It is impossible to determine whether an object is a proxy or not. – 2ality

According to the Javascript language specifications, **there is no way to determine if an Object is a Proxy**. However, on Node 10+ it is possible using the [`util.types.isProxy`](https://nodejs.org/api/util.html#util_util_types_isproxy_value) method.

## What’s the target?

Given a Proxy object, **it’s not possible to obtain or change the target object**. It is also not possible to obtain or modify the handler object.

The closest approximation is in Ben Nadel’s article [Using Proxy to Dynamically Change THIS Binding](https://www.bennadel.com/blog/3262-using-proxy-objects-to-dynamically-change-the-this-binding-within-a-generator-in-node-js.htm), which uses an empty object as the Proxy target and closures to cleverly reassign the object the Proxy’s actions are performed on.

## Proxy Primitives

Unfortunately, one limitation of Proxy is that **the target has to be an Object**. That means we cannot use primitives like String directly. 😞

## Performance

[A major drawback of Proxy is performance](http://thecodebarbarian.com/thoughts-on-es6-proxies-performance). Impact will vary based on browser and use, but **Proxy isn’t the best approach for performance-critical code**. Of course you can always measure the impact and decide if the advantages of Proxy outweigh the affect on performance.

## Why Proxy?

Proxy provides a virtualized interface to control the behavior of any target Object. In doing so, it strikes a balance between simplicity and utility without sacrificing compatibility. Any code that expects an Object can accept a Proxy.

Perhaps the most compelling reason to use Proxy is that many of the examples above are just a few lines long and can easily be composed to create complex functionality. For one final example, we can compose functions from several use cases to create a read-only cookie object that returns a default value for non-existent or “private”, hidden cookies.

{% gist https://gist.github.com/Tombarr/d3c4a7720499df1b6dbc790b8fb8a554 %}

I hope these examples have shown that Proxy is more than just an esoteric feature for niche metaprogramming in Javascript.

## Resources

* [10 Use Cases for Proxy](http://dealwithjs.io/es6-features-10-use-cases-for-proxy/)
* [MDN–Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)
* [David Walsh–JavaScript Proxy](https://davidwalsh.name/javascript-proxy)
* [Intro to Javascript Proxy Object](https://blog.campvanilla.com/advanced-guide-javascript-proxy-objects-introduction-301c0fce9432)
* [2ality–Metaprogramming with ES6 proxies](http://2ality.com/2014/12/es6-proxies.html)