back to the lesson

Observable

Create a function makeObservable(target) that “makes the object observable” by returning a proxy.

Here’s how it should work:

function makeObservable(target) {
  /* your code */
}

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

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

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

In other words, an object returned by makeObservable has the method observe(handler).

Whenever a property changes, handler(key, value) is called with the name and value o the property.

P.S. In this task, please handle only writing to a property. Other operations can be implemented in a similar way. P.P.S. You might want to introduce a global variable or a global structure to store handlers. That’s fine here. In real life, such function lives in a module, that has its own global scope.

The solution consists of two parts:

  1. Whenever .observe(handler) is called, we need to remember the handler somewhere, to be able to call it later. We can store it right in the object, using our symbol as the key.
  2. We need a proxy with set trap to call handlers in case of any change.
let handlers = Symbol('handlers');

function makeObservable(target) {
  // 1. Initialize handlers store
  target[handlers] = [];

  // Store the handler function in array for future calls
  target.observe = function(handler) {
    this[handlers].push(handler);
  };

  // 2. Create a proxy to handle changes
  return new Proxy(target, {
    set(target, property, value, receiver) {
      let success = Reflect.set(...arguments); // forward the operation to object
      if (success) { // if there were no error while setting the property
        // call all handlers
        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";