Using javascript proxy

I have recently been digging into proxy in Javascript. I was surprised by what it can do. Proxy allows you to hook into fundamental operations of the language constructs (like object, arrays, functions..)

Here is list of simple usecases for javascript proxy

Freezing Object

We can prevent mutation of objects easily with few lines of code essentially freezing the object

const hero = {
  name: 'Saitama',
  age: 25,
  class: 'B',
  race: 'Human',
  heroName: 'Caped Baldy',
};

const Freeze = (obj) =>
  new Proxy(obj, {
    set: function (target, key, value) {
      throw Error("You can't change values this object has been frozen");
    },
  });

const saitama = Freeze(hero);
saitama.name = 'Garuro'; // This will throw error

You can also freeze arrays, Map, WeakMap, Set...

Tracking changes on object

Proxy can be used track changes to object essentially maintain history.

const createHistory = (obj) => {
  let history = [JSON.parse(JSON.stringify(obj))];
  const proxiedObject = new Proxy(obj, {
    set: function (target, key, value) {
      history.push({ ...target, [key]: value });
      Reflect.set(target, key, value);
    },
  });
  return [history, proxiedObject];
};

const [history, proxiedObject] = createHistory(hero);
proxiedObject.name = 'Genos';
proxiedObject.class = 'C';

console.log(history);

history object will contains snapshot of changes made to the object

[
  {
    name: 'Saitama',
    age: 25,
    class: 'B',
    race: 'Human',
    heroName: 'Caped Baldy',
  },
  {
    name: 'Genos',
    age: 25,
    class: 'B',
    race: 'Human',
    heroName: 'Caped Baldy',
  },
  {
    name: 'Genos',
    age: 25,
    class: 'C',
    race: 'Human',
    heroName: 'Caped Baldy',
  },
];

similarly you can also track number of times function was called with apply trigger.

Case insensitive key access

const caseInsensitive = (obj) =>
  new Proxy(obj, {
    get: function (target, key) {
      // I am assuming keys are lowercase by default for simplicity
      return Reflect.get(target, key.toLowerCase());
    },
  });

const proxiedObject = caseInsensitive(hero);
console.log(proxiedObject.name); // saitama
console.log(proxiedObject.NAME); // saitama

Handling properties that does not exist

You can easily handle undefined properties by following snippet

const FetchValue = (obj) =>
  new Proxy(obj, {
    get: function (target, key) {
      if (target.hasOwnProperty(key)) {
        return [Reflect.get(target, key), true];
      }
      return [null, false];
    },
  });

const proxiedObject = FetchValue(hero);

const [name, nameExist] = proxiedObject.name;
const [city, cityExist] = proxiedObject.city;

if (nameExist) {
  console.log(name);
}

if (cityExist) {
  console.log(city); // this is not executed since property city does not exist
}

You can do much more with proxy than what is listed here. You can look up more awesome usage here https://github.com/mikaelbr/awesome-es2015-proxy

This post is also available on DEV.