regresar a la lección

Observable

Crea una función makeObservable(target) que “haga el objeto observable” devolviendo un proxy.

Así es como debe funcionar:

function makeObservable(target) {
  /* tu código */
}

let user = {};
user = makeObservable(user);

user.observe((key, value) => {
  alert(`SET ${key}=${value}`);
});

user.name = "John"; // alerta: SET name=John

En otras palabras, un objeto devuelto por makeObservable es como el original pero que también tiene el método observe(handler) que establece una función handler que será llamada en cualquier cambio de propiedad.

cada vez que una propiedad cambie, handler(key, value) es llamado con el nombre y el valor de la propiedad.

P.D. En esta tarea, solo toma en cuenta la escritura de una propiedad. Otras operaciones pueden ser implementadas de manera similar.

La solución consiste de dos partes:

  1. Cuando .observe(handler) es llamado, necesitamos recordar el manejador ‘handler’ en algún lugar para poder llamarlo después. Podemos almacenar los manejadores directamente en el objeto, usando nuestro symbol como clave de la propiedad.
  2. Necesitamos un proxy con la trampa set que llame a los manejadores en caso de cualquier cambio.
let handlers = Symbol('handlers');

function makeObservable(target) {
  // 1. Inicializa el almacén de manejadores
  target[handlers] = [];

  // Almacena la función manejadora en el array para llamadas futuras
  target.observe = function(handler) {
    this[handlers].push(handler);
  };

  // 2. Crea un proxy para manejar cambios
  return new Proxy(target, {
    set(target, property, value, receiver) {
      let success = Reflect.set(...arguments); // reenvía la operación al objeto
      if (success) { // si no hay errores al establecer la propiedad
        // llama a todos los manejadores
        target[handlers].forEach(handler => handler(property, value));
      }
      return success;
    }
  });
}

let user = {};

user = makeObservable(user);

user.observe((key, value) => {
  alert(`SET ${key}=${value}`);
});

user.name = "John";