Proxy in JS: what the hell?
Tags: javascript, tutorial, beginners
Published: 2022-04-20 19:59:30

**Proxy** is something that is commonly used in Java, for example in **Spring** with **Aspect-Oriented Programming** (`@Transactional`, `@Security`, ....). But in Javascript, it's not something that it is usually used. Or is it? :)

In this article, you are going to see:
- the principle
- the API
- some examples of what we can do
- performances
- which libraries use it


## Principle

The idea is that we are going to create a new object that wraps the original one, and intercepts and redefines fundamental operations such as getting / setting a property, executing a function , ...

More precisely, we are going to proxify a **target** object (an `object`, `function`, ...). And define the operations that we want to intercept / reimplement thanks to an object named **handler** that contains **traps** (for example `get`, `set`, ...).

Here is an image to resume this:

![Javascript proxy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j2krr3s2f5p6jc1i4z3l.png)

----

## API

Now it's time to see how to implement **Proxy** in JS. The API is simple

```javascript
const proxy = new Proxy(target, handler);
```

The `handler` is an object containing all the `traps` we want to intercept:

```javascript
const handler = {
  get(target, prop, receiver) {
    console.log("Getting the property", prop);
    return target[prop];
  },
  set(target, prop, value) {
    console.log("Setting the property", prop);
    target[prop] = value;
    return true;
  },
};
```

> **Note:** You can also use the `Reflect` API to implement the proxy.


**Revocable Proxy**

It's also possible to create a Proxy that can be revoked thanks to a given function. Once revoked the proxy becomes unusable.

```javascript
// `revoke` is the function to revoke the proxy
const { proxy, revoke } = Proxy.revocable(target, handler);
```

> **Note:** It's not able to restore the Proxy once revoked.


And here we go. You know the API, let's see some example we can implement.

----

## Example of trap

### Get

Let's start with the `get` trap. Its API is:

```javascript
get(target, property, receiver)
```

For the example we are going to do a proxy that will throw an error if the user tries to access a property that doesn't exist on the object.

```javascript
const person = {
  firstName: "Bob",
  lastName: "TheSponge",
};

const personProxy = new Proxy(person, {
  get(target, prop) {
    if (!prop in target) {
      throw new Error(
        `The property ${prop} does not exist in the object`
      );
    }

    return target[prop];
  },
});

// Will print: 'Bob'
console.log(personProxy.firstName);

// Will throw an error
console.log(personProxy.unknownProp);
```

---

### Apply

The `apply` trap is the following one:

```javascript
apply(target, thisArg, argumentsList)
```

To illustrate it, we are going to implement a `withTimerProxy` that will measure the duration of a callback execution.

```javascript
function aFunction(param) {
  console.log("The function has been called with", param);

  return "Hello " + param;
}

function withTimerProxy(callback) {
  return new Proxy(callback, {
    apply(target, thisArg, argumentsList) {
      console.time("Duration");

      const result = callback.apply(thisArg, argumentsList);

      console.timeEnd("Duration");

      return result;
    },
  });
}

const aFunctionWithTimer = withTimerProxy(aFunction);

// Will print:
// The function has been called with World
// Duration: 0.114013671875 ms
// 'Hello World'
console.log(aFunctionWithTimer("World"));
```

> **Note:** If you want to see more console API you can read my article [Unknown console API in JS](https://dev.to/romaintrotard/unknown-console-api-in-js-2mek).

> **Note:** To do such a functionality, we would probably use an **High Order Function** instead.

----

### Other traps

Here is the exhaustive list of trap you can use:

* `construct(target, argumentsList, newTarget)`
* `defineProperty(target, property, descriptor)`
* `deleteProperty(target, property)`
* `getOwnPropertyDescriptor(target, prop)`
* `getPrototypeOf(target)`
* `has(target, prop)`
* `isExtensible(target)`
* `ownKeys(target)`
* `preventExtensions(target)`
* `set(target, property, value, receiver)`
* `setPrototypeOf(target, prototype)`


----

## Performances?

Recently, I have seen in the `react-hook-form` implementation, that Bill decided not to use `Proxy` anymore for the tracking of who watch the state of the form because of performances reasons. 

Are the performances so bad? Let's try to measure the performance cost when retrieving the value of a simple property.


I will use the `benchmark` library. Here is the script I will run:

```javascript
const Benchmark = require("benchmark");

const suite = new Benchmark.Suite();

const person = {
  firstName: "Bob",
  lastName: "TheSponge",
};

const personProxy = new Proxy(person, {});

suite
  .add("native", () => {
    person.firstName;
  })
  .add("proxy", () => {
    personProxy.firstName;
  })
  .on("cycle", (event) =>
    console.log(event.target.toString())
  )
  .run({ async: true });
```

The result is the following one:

![Performance result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5l4lhj32c3n111gpmkno.png)
 
Of course, the native implementation is faster because it just access the property. The proxy implementation is largely slower than the native one. But I think it's not so bad.

If you search on the internet about, performances of proxy, some people say that it's a tool for development and should not be used in production. Personally, I think it depends on your use case, the amount of data you want to process with Proxy and the performance you want to have. You can test that with a **Proof Of Concept** (POC).
There are libraries that rely on proxies, which proves that this can be used in production. Let see two of them.

> **Note:** It's good to note that the "selling point" of **`react-hook-form`** is the performance, so it makes sense not to use **Proxy**. 

----

## Real use case 

### SolidJS 

**SolidJS** is a declarative library to build UI, that relies on fine grained reactivity. It does not use a virtual DOM (contrary to **React**).
The way of writing the code is quite similar to React:
* JSX
* Component
* state => signal
* useEffect => createEffect
* useMemo => createMemo
* ...

But there is no hook rules, you should not destructure your props, every components executes ones then it will execute side effect when a used reactive primitive has changed.
It uses **Proxy** for **store** which is the equivalent of **React reducers**.
If you don't know **SolidJS**, go check it has a promising future.

For example here is a simple `Counter` component:

```jsx
import { createSignal } from 'solid-js';

function Counter() {
  const [count, setCount] = createSignal(0);
  const increment = () => setCount(count() + 1);

  return (
    <button type="button" onClick={increment}>
      {count()}
    </button>
  );
}
```

> **Note:** This is not an exhaustive list of similarities / differences and how to use the library. When I feel more confident, I will try to write an article about it because I think it has a bright future ahead of it.

----

### ImmerJS

**ImmerJS** allows us to create immutable data structures, but giving us the possibility to change its data in a mutable way.

For example, you will able to do:

```javascript
import product from "immer";

const person = {
  firstName: "Bob",
  lastName: "TheSponge",
};

const newPerson = produce(person, (p) => {
  p.firstName = "Patrick";
  p.lastName = "Star";
});

// Will return false
console.log(newPerson === person);
```

It's a convenient way to simplify changes without mutates an object, and without to make a lot of copy.

```javascript
const person = {
  firstName: "Bob",
  lastName: "TheSponge",
  address: {
    type: "pineapple",
    city: "Bikini bottom",
  },
};

// It prevents us to make things like
const newPerson = {
  ...person,
  address: {
    ...person.address,
    // Note the uppercase
    type: "Pineapple",
  },
};
```

> **Note:** `Redux Toolkit` uses it under the hood to make reducers easier to implement.

----

## Conclusion

**Proxy** enables a lot of features, thanks to them you can extract some logic that will be reusable. Some libraries use them to optimize your code execution. For example [`react-tracked`](https://github.com/dai-shi/react-tracked) and [`proxy-memoize`](https://github.com/dai-shi/proxy-memoize) that use, [`proxy-compare`](https://github.com/dai-shi/proxy-compare) under the hood, will reduce your re-render. These libraries are developed by **Dai Shi** who also made the `use-context-selector` library that I demystified in the article [use-context-selector demystified](https://dev.to/romaintrotard/use-context-selector-demystified-4f8e).
But I would recommend you to use them uniquely when it's necessary.

There is a **[TC39 proposal](https://github.com/tc39/proposal-decorators)** to add natively **Decorators** to javascript, looks promising, which will enable some other possibilities. For example to log / measure performance of function, ... in an easy way: with a `@Logger` or `@Performance` (with the right implementation behind these annotations).

----

Do not hesitate to comment and if you want to see more, you can follow me on [Twitter](https://twitter.com/romain_trotard ) or go to my [Website](https://www.romaintrotard.com).