Proxy Objects in JavaScript
Tags: javascript, programming
Published: 2022-08-18 21:58:29

Proxy objects were unknown territory for me just a little while ago. I had never even heard of them until I was asked to explain how to use them over on TikTok. I've spent some time now reading up on how to create proxy objects and how they can be used. I'm going to try my best to keep this brief so this won't be a comprehensive guide on how to use proxy objects. This will however show you some of the basic ways you can use them, as well as provide a few examples.

## Creating a Proxy Object

To create a proxy object you'll need two things first, a target object and a handler object. The target is the object you're wanting to make the proxy for and the handler is where you define what behavior to change and how it should be changed. After defining both of those objects, you can create the proxy by creating a variable set equal to `new Proxy(target, handler)` replacing target and handler with whatever you've named them.

```js
const target = {
  greetingOne: "hello",
  greetingTwo: "world",
};

const handler = {};

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

I've left the handler empty for now, meaning that there won't be any changes in behavior. This isn't very useful, so next we'll start looking at how we can change some of the behavior of the proxy.

## Changing The Get Method

Let's say that we want our proxy to change `greetingTwo` to 'dev.to' if `target.greetingTwo` is equal to 'world'. To do this, we'll redefine the get method in our handler object to overwrite the normal behavior. We'll need to pass target, prop and receiver into a `get()` function and then use those parameters to change the behavior.

```js
const target = {
  greetingOne: "hello",
  greetingTwo: "world",
};

const handler = {
  get(target, prop, receiver) {
    if (prop === "greetingTwo") {
      return "dev.to";
    }
    return Reflect.get(...arguments);
  },
};

const proxy = new Proxy(target, handler);

console.log(proxy.greetingTwo); // dev.to
```

Using `Reflect` we can keep the original behavior for some of the accessors and only change the values we're wanting to change.

## Changing The Set Method

What if we only want certain values to be valid on an object? In order to accomplish this, we can use `set()` in our handler. This time we'll pass in an object, prop and value and use the parameters to make sure our data is valid.

```js
const target = {
  value: 1,
};

const handler = {
  set(obj, prop, value) {
    if (prop === "value" && value < 1) {
      console.log("value cannot be lower than 1");
      // handle error
    } else {
      return Reflect.set(...arguments);
    }
  },
};

const proxy = new Proxy(target, handler);

proxy.value = 0; // 'value cannot be lower than 1'
console.log(proxy.value); // 1

proxy.value = 3;
console.log(proxy.value); // 3
```

Again, using `Reflect` let's us keep the normal behavior if we want it. Attempting to set `proxy.value` to a number less than 1 will result in a warning being console logged and the value not being set. Values 1 and higher are still valid.

## Intercepting other methods

In hopes of keeping this post short and concise I'm not going to cover each individual handler trap. There are eleven more, but I'll only be showing a few. MDN has examples for all of them, and I would reccomend giving the docs a quick read if you're looking for more examples and a more thorough explanation.

- `apply()`

  - Using `apply()` traps function calls.
  - Using a function as your target object, and calling `apply()` like this:

  ```js
  apply: function(target, thisArg, argList){}
  ```

  you can add onto existing functions, changing how a function behaves.

- `construct()`

  - Using `construct(target, args)` traps the 'new' keyword.
  - You're then able the change the behavior of constructing new objects

- `has()`
  - Using `has(target, key)` is a trap for the `in` operator
  - You're then able to change the behavior of how keys are accessed through the `in` operator like this
  ```js
  if (key === "keyOne") {
    return false;
  }
  return key in target;
  ```
- `deleteProperty()`

  - Using `deleteProperty(target, property)` traps the `delete` operator
  - This will allow you to change how properties get deleted, allowing for conditions to be set

  ```js
  delete proxy.keyOne; // gets trapped in the handler object
  ```

That's going to do it for this post, If you have any questions I'll do my best to answer them in the comments.