DEV Community

Play Button Pause Button
Caleb Weeks
Caleb Weeks

Posted on

Fun with JavaScipt proxies: observability, extensible chaining, and callable objects

I've been having a lot of fun playing with JavaScript proxies lately. It is fun to come up with creative ways of using them. In today's JavaScript meetup, we took a look at using proxies to handle obversability, create chainable APIs, and define callable objects.

The code for the proxies discussion can be found below and here

const helpers = require("./helpers");
const { log } = helpers;

// Proxies provide transparent obeservability
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const proxy = new Proxy(array, {});
log(array, proxy);
log(typeof array, typeof proxy);
log(
  Object.getOwnPropertyNames(array), 
  Object.getOwnPropertyNames(proxy)
);

//////// Observability ////////
const onUpdate = (obj) => (callback) => {
  return new Proxy(obj, {
    set: (_target, _prop, value) => {
      callback(value);
      Reflect.set(...arguments);
    },
  });
};

const person = onUpdate({
  firstName: "George",
  lastName: "Washington",
})(log);

person.lastName = "Clooney";

//////// Chaining outside functions ////////
const chain = (fns) => (obj) => {
  const isFunction = (fn) => typeof fn === "function";
  const chainify = (obj) => {
    if (typeof obj === "object" && obj !== null) {
      return new Proxy(obj, {
        get: (target, prop) => {
          switch (true) {
            case isFunction(target[prop]):
              return (...args) =>
                chainify(target[prop].apply(target, args));
            case isFunction(fns[prop]):
              return (...args) =>
                chainify(fns[prop].apply(target, [target, ...args]));
            default:
              return target[prop];
          }
        },
      });
    }
    return obj;
  };
  return chainify(obj);
};

chain(helpers)(array)
  .map((i) => i + 10)
  .log()
  .shuffle()
  .log()
  .zipWith(["a", "b", "c", "d", "e"], (a, b) => a + b)
  .log();

//////// Call object as a function ////////
const callable = (obj) =>
  new Proxy(() => {}, {
    apply(_target, _, [prop, ...args]) {
      return obj[prop] || obj._(prop);
    },
  });

const fib = callable({
  0: 1,
  1: 1,
  _: (n) => fib(n - 1) + fib(n - 2),
});

chain({ fib, log })(array)
  .map((i) => fib(i))
  .log();
Enter fullscreen mode Exit fullscreen mode

Latest comments (1)

Collapse
 
artydev profile image
artydev

Great thank you