Проксирование изменений вложенного объекта в JavaScript?

Доброго времени суток, %username%!

Итак, JavaScript. У нас имеется объект (с методами загрузки/сохранения) для хранения настроек приложения (в localstorage), и объект этот выглядит так:

var SettingsStorage = {
  data: {
    some_value1: null,
    child_object: {
      some_value2: true,
      some_value3: 1000
    },
    some_value4: null
  },
  load: function(callback) {
    // load this.data from localstorage
  },
  save: function(callback) {
    // save this.data to localstorage
  }
};


И имеется прокси-объект для доступа к данным настроек вида:
var Settings = new Proxy(SettingsStorage.data, {
  set: function(obj, prop, value) {
    var storage_data = SettingsStorage.data;
    if (obj === storage_data && prop === 'some_value1') {
      console.log('Да да! Мы поймали момент изменения SettingsStorage.data.some_value1!');
    }
    obj[prop] = value;
    return true;
  }
});


И теперь при изменении Settings.some_value1 мы изменяем SettingsStorage.data.some_value1 и можем выполнить произвольный код в момент изменения (например - сохранить изменения, что избавит нас от принудительного вызова SettingsStorage.save()).

И всё бы ничего, если бы не необходимость перехватить момент изменения SettingsStorage.data.child_object.some_value2 - с дочерними объектами этот финт не прокатывает. Как это сделать?

ps. Данный подход необходим для того, что-бы при изменении каких-либо важных настроек приложения изменить его поведение в целом. Другими словами нужно отловить изменения свойств объекта, без написания отдельного объекта с тучей геттеров/сеттеров + переписывать уже созданный апи. Браузер - Google Chrome.
  • Вопрос задан
  • 396 просмотров
Решения вопроса 2
https://jsfiddle.net/g1kebLbg Всё очень сложно. На это ушло 2 часа и я всё-равно не уверен, что нет багов.
Ответ написан
Комментировать
paramtamtam
@paramtamtam Автор вопроса
В общем пошел по пути не самому лучшему, но более стабильному. Все настройки привел к "плоскому" виду, и обернул всё это дело в прокси для отлова событий. Так и к опциям общения происходят "прямо" - Settings.value1, и к методам Settings.onSet. По хорошему это лютый говнокод, но как лучше сделать - ещё не придумал.

var Settings = new Proxy({
  data: {
    value1: 'some text',
    value2: true,
  },

  /**
   * Callback - on any property/value read
   *
   * @param   {string} name
   * @returns {void}
   */
  onGet: function(name) {
    console.log('Property "' + name + '" read action');
  },

  /**
   * Callback - on any property/value write
   *
   * @param   {string} name
   * @param   {mixed} value
   * @returns {void}
   */
  onSet: function(name, value) {
    console.log('Property "' + name + '" write "' + value + '" action');
  },

  /**
   * Load settings from storage
   *
   * @param   {callable} callback
   * @returns {void|object}
   */
  load: function(callback) {
    // Code
  },

  /**
   * Save settings in storage
   *
   * @param   {callable} callback
   * @returns {void}
   */
  save: function(callback) {
    // Code
  },

  /**
   * Remove all settings from storage
   *
   * @param   {callable} callback
   * @returns {void}
   */
  clear: function(callback) {
    // Code
  }
}, {
  /**
   * Getter
   *
   * @param   {object} target
   * @param   {string} name
   * @returns {mixed}
   */
  get: function(target, name) {
    var result = undefined;
    if (name in target.data) {
      result = target.data[name];
    }
    if (name in target) {
      result = target[name];
    }
    if (typeof target.onGet === 'function') {
      target.onGet.call(target, name);
    }
    return result;
  },

  /**
   * Setter
   *
   * @param   {object} target
   * @param   {string} name
   * @param   {mixed} value
   * @returns {mixed}
   */
  set: function(target, name, value) {
    if (name in target.data) {
      target.data[name] = value;
      // Save changes in storage
      target.save.call(target, name, value);
    }
    if (name in target) {
      target[name] = value;
    }
    if (typeof target.onSet === 'function') {
      target.onSet.call(target, name, value);
    }
    return true;
  }
});
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы