回到课程

可观察的(Observable)

创建一个函数 makeObservable(target),该函数通过返回一个代理“使得对象可观察”。

其工作方式如下:

function makeObservable(target) {
  /* 你的代码 */
}

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

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

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

换句话说,makeObservable 返回的对象就像原始对象一样,但是具有 observe(handler) 方法,该方法可以将 handler 函数设置为在任何属性被更改时,都会被调用的函数。

每当有属性被更改时,都会使用属性的名称和属性值调用 handler(key, value) 函数。

P.S. 在本任务中,你可以只关注属性写入。其他的操作可以通过类似的方式实现。

该解决方案包括两部分:

  1. 无论 .observe(handler) 何时被调用,我们都需要在某个地方记住 handler,以便以后可以调用它。我们可以使用 Symbol 作为属性键,将 handler 直接存储在对象中。
  2. 我们需要一个带有 set 陷阱的 proxy 来在发生任何更改时调用 handler。
let handlers = Symbol('handlers');

function makeObservable(target) {
  // 1. 初始化 handler 存储
  target[handlers] = [];

  // 将 handler 函数存储到数组中,以便于之后调用
  target.observe = function(handler) {
    this[handlers].push(handler);
  };

  // 2. 创建一个 proxy 以处理更改
  return new Proxy(target, {
    set(target, property, value, receiver) {
      let success = Reflect.set(...arguments); // 将操作转发给对象
      if (success) { // 如果在设置属性时没有出现 error
        // 调用所有 handler
        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";