When to use ES6 proxies?

One of the most cool features of ES6 are proxies. Proxies are a special kind of objects to alter the semantics of other objects: access, deletion, setting, call semantics

As a meta-programming lover, I’m eager to use proxies for some fancy tricks. The other day I had the chance to use proxies and property getters while implementing an EventEmitter trait. I want to share the implementation to illustrate the differences between intercepting `get` semantics and using a `getter`.

Consider this:

function newEmitter() {
  var __handlers = new Symbol();
  return {
    on(type, handler) {
      this[__handlers][type].add(handler);
    },
    off(type, handler) {
      this[__handlers][type].delete(handler);
    },
    emit(type, ...args) {
      this[__handlers][type].forEach(handler => handler.call(this, ...args));
    },
    get [__handlers]() {
      var handlerSet = newHandlerSet();
      Object.defineProperty(this, __handlers, { value: handlerSet });
      return handlerSet;
    }
  };
}
function newHandlerSet() {
  return new Proxy(Object.create(null), {
    get(target, property) {
      if (!target[property]) { target[property] = new Set(); }
      return target[property];
    }
  });
}

Yep, the getter is gratuitous, it could be simply a computed property with the proxy but let do it this way for learning purposes.

Function `newEmitter()` creates an object ready to be mixed into an object prototype to use it as an event emitter. Methods `on()`, `off()` and `emit()` are trivially simple due to a couple of assumptions:

  1. The `__handlers` property will always be valid.
  2. Entries of the `__handlers` properties are always sets.

This allow us to avoid checkings on these properties and keep code simple.

Notice both functions, the getter and the get trap in the proxy give a solution for the same problems: to provide a default value for some properties. The difference is the nature of the knowledge we have about these properties. In case of the getter we want `__handlers` to default to a handler set so the getter overrides itself with an empty handler set. We specifically know which property we want to access in a special way and we implement the behaviour locally.

In case of the get trap we are altering not an specific property of an object but all the future properties. Actually, we are defining a new kind of object where all properties must be sets so we are changing the whole semantics of the object: as soon as we access a property, we obtain a set, if the property does not exist yet, we obtain an empty set. We implement this behaviour globally for all present and future properties.

You use getters to compute properties. While you use get traps to alter semantics. Do you know how to calculate an specific attribute? Use a getter for that attribute. Do you want to change the fact of accessing properties, in general, use the get trap.

Oh! And please, do not use proxies for controlling access or extending or restricting APIs, you already have inheritance and composition for that!

Proxies are really powerfull. I hope to show you more usages soon.

Deja una respuesta

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s