DEV Community

Julia Shlykova
Julia Shlykova

Posted on

JavaScript Proxy objects

The Proxy object is used in place of the original object. It is used to create some kind of filter of the object: it can determine what kind of values can be set, what properties can be deleted, whether can we even get some value or they are restricted.

Basic structure

To create proxy we use syntaxis: Proxy(target, handler):

  • target - the original object, we want to proxy;
  • handler - an object consisted of traps, that determine which operations are intercepted and how to redefine them.

Let's look at the example with empty handler (all operations are implemented on the original object):

const target = {
  name: "Mary",
  status: "Online"
};

const handler = {};

const proxy = new Proxy(target, handler);

proxy.name = "Ann";

console.log(proxy.name); // Ann

for (let key in proxy) {
  console.log(key); // name status
}
Enter fullscreen mode Exit fullscreen mode
  1. proxy.name = sets value on target;
  2. proxy.name returns value from target;
  3. Iteration of keys in proxy returns keys from target.

Without handler, proxy just transfers all operations on target.

Handler

The most used traps in handler:

  • get - reading properties in target;
  • set - setting values to properties in target;
  • deleteProperty - controling deletion of properties

Trap "get"

The get trap takes the following arguments:

  • target - the same target from Proxy argument;
  • prop - the property;
  • receiver - this value. Usually it's either the proxy or an object that inherits from the proxy

Let's create some simple handler:

const target = {
  name: "Mary",
  status: "Online"
};

const handler = {
  get(target, prop) {
    if (prop in target) {
      return target[prop];
    } else {
      return "no info";
    }
  }
};

const proxy = new Proxy(target, handler);

console.log(proxy.name); // Ann
console.log(proxy.contact); // no info
Enter fullscreen mode Exit fullscreen mode

Trap "set"

This trap is useful when we want some validation before setting values. It takes the same arguments as get plus value:

  • target - the same target from Proxy argument;
  • prop - the property;
  • value - the value to set for the property;
  • receiver - this value. Usually it's either the proxy or an object that inherits from the proxy
const object = {
  name: "Mary",
  status: "Online"
};

const handler = {
  set(target, prop, value) {
    if (prop in target && typeof value === 'string') {
      target[prop] = value;
      return true;
    } else {
      return false;
    }
  }
};

const proxy = new Proxy(object, handler);

proxy.name = "Ann";
proxy.status = false; // can't set since the value type is boolean

console.log(proxy.name); // Ann
console.log(proxy.status); // Online
Enter fullscreen mode Exit fullscreen mode

set has to return true if we write the value and false if there is an error;

Trap "deleteProperty"

Let's say we don't want the password to be accessed anyhow in our object:

const user = {
  name: "Mary",
  _password: "******"
};

const handler = {
  get(target, prop) {
    if (prop === "_password") {
      throw new Error("Access denied")
    } else {
      return target[prop];
    }
  },
  set(target, prop, value) {
    if (prop === "_password") {
      throw new Error("Access denied")
    } else {
      target[prop] = value;
      return true;
    }
  },
  deleteProperty(target, prop) {
    if (prop === "_password") {
      throw new Error("Access denied")
    } else {
      delete target[prop];
      return true;
    }
  }
}

const proxyUser = new Proxy(user, handler);

try {
  console.log(proxyUser._password);
} catch(e) {
  console.log(e.message);
}

// Access denied
Enter fullscreen mode Exit fullscreen mode

Private properties

Let's say we have some method that reaches the private property:

const user = {
  name: "Mary",
  _password: "******",
    getPassword() {
        return this._password;
    }
};

const handler = {
  get(target, prop) {
    if (prop.startsWith("_")) {
      throw new Error("Access denied")
    } 
        return target[prop];
  }
}

const proxyUser = new Proxy(user, handler);

try {
  console.log(proxyUser.getPassword());
} catch(e) {
  console.log(e.message);
}

// "Access denied"
Enter fullscreen mode Exit fullscreen mode

Here, we will get error and denied access.

What if we want our method to work? We have to bind this not to the proxy but to the target. Then the method will be called upon the target, not proxy:

const handler = {
  get(target, prop) {
    if (prop.startsWith("_")) {
      throw new Error("Access denied")
    } 
    const value = target[prop];
    return (typeof value === 'function') ? value.bind(target) : value;
  }
}
Enter fullscreen mode Exit fullscreen mode

Now upon calling proxyUser.getPassword() we will get "******".

Top comments (0)