돌아가기

Observable 만들기

프락시를 반환해 ‘객체를 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가 반환하는 객체는 기존 객체와 동일하지만 프로퍼티가 변경될 때 호출되는 함수인 handler를 설정해주는 메서드 observe(handler)가 있어야 합니다.

프로퍼티가 변경될 때마다 프로퍼티 키와 값을 인수로 받는 메서드 handler(key, value)가 호출되어야 하죠.

참고: 이 문제에선 프로퍼티에 값을 쓰려는 경우만 고려해서 답을 작성해보세요. 읽기 등의 동작은 유사한 방법을 사용해 구현할 수 있습니다.

해답은 크게 두 부분으로 구성됩니다.

  1. .observe(handler)이 호출될 때마다 핸들러를 어딘가에 보관해두고 나중에 호출될 수 있도록 해야 하는데, 심볼을 프로퍼티 키로 사용해 핸들러를 객체에 저장할 수 있게 해 보았습니다.
  2. 변경이 있을 때마다 핸들러가 호출되도록 set 트랩이 있는 프락시를 만들어 보았습니다.
let handlers = Symbol('handlers');

function makeObservable(target) {
  // 1. 핸들러를 저장할 곳을 초기화합니다.
  target[handlers] = [];

  // 나중에 호출될 것을 대비하여 핸들러 함수를 배열에 저장합니다.
  target.observe = function(handler) {
    this[handlers].push(handler);
  };

  // 2. 변경을 처리할 프락시를 만듭니다.
  return new Proxy(target, {
    set(target, property, value, receiver) {
      let success = Reflect.set(...arguments); // 동작을 객체에 전달합니다.
      if (success) { // 에러 없이 프로퍼티를 제대로 설정했으면
        // 모든 핸들러를 호출합니다.
        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";