Почему Object.observe() не предоставляет путь изменения данных для обратного вызова?

Массив изменений обратного вызова Object.observe() содержит объекты со следующими четырьмя свойствами:

  • название
  • объект
  • тип
  • старое значение

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/observe#Parameters

Почему изначально нет path? Пример:

var ob = {
    foo: [
        {moo: "bar", val: 5},
        {val: 8}
    ]
}

ob.foo[0].val = 1;
// callback should provide path "foo.0.val" or "foo[0].val"

Существует модуль Node.js, который расширяет Object.observe() и включает также путь: observed.js,
но Я беспокоюсь, что прирост производительности родного observe() будет потерян (если нет, не могли бы вы объяснить, как это реализовано тогда?). Возможно, браузерировать модуль, но я не могу представить, что он будет хорошо работать в синхронной среде, и мне все еще интересно почему никто, похоже, не подумал о дополнительном свойстве path.


person CodeManX    schedule 06.11.2014    source источник


Ответы (1)


Потому что нет четкого пути.

Рассмотрим следующее:

var movall = {moo: "bar", val: 5};
var ob1    = {a: mooval};
var ob2    = {b: movall};

Теперь предположим, что я наблюдаю movall. Затем я обновляю moo. Что такое путь? Это movall.moo, или ob1.a.moo, или ob2.b.moo? Если я наблюдаю ob1, об изменении не сообщается, поскольку нет изменений ни в одном из его свойств (изменение было внутренним в одном из его свойств, что не считается).

Объекты не зависят от их существования, вложенного в другие объекты. Они могут быть вложены в несколько других объектов. Не существует уникального «пути», описывающего, как перейти от потенциально нескольких начальных точек к определенному свойству, которое могло измениться.

JS также не знает пути, по которому вы пришли к изменяемому свойству. Таким образом, в ob.foo[0].val = 1; JS просто оценивает цепочку, достигает объекта foo[0], изменяет его свойство val и в этот момент понятия не имеет, как он пришел к foo[0]. Все, что он знает, это то, что foo[0] изменился. Оно изменилось в пределах ob, но могло также измениться и в каком-то другом объекте, у которого есть свойство foo[0].

Тем не менее, вы, возможно, можете добиться того, чего, кажется, пытаетесь добиться, создав некоторый механизм поверх низкоуровневого механизма наблюдения/уведомления. Мы определим функцию для объекта, которая рекурсивно устанавливает наблюдателей для его объектов свойств и так далее, и распространяет записи об изменениях обратно с правильно построенными путями:

function notifySubobjectChanges(object) {
  var notifier = Object.getNotifier(object);        // get notifier for this object
  for (var k in object) {                           // loop over its properties
    var prop = object[k];                           // get property value
    if (!prop || typeof prop !== 'object') break;   // skip over non-objects
    Object.observe(prop, function(changes) {        // observe the property value
      changes.forEach(function(change) {            // and for each change
        notifier.notify({                           // notify parent object
          object: change.object,                    // with a modified changerec
          name: change.name,                        // which is basically the same
          type: change.type, 
          oldValue: change.oldValue, 
          path: k + 
            (change.path ? '.' + change.path : '')  // but has an addt'l path property
        });
      });
    });
    notifySubobjectChanges(prop);                   // repeat for sub-subproperties
  }
}

(Примечание: объект change заморожен, и мы не можем ничего добавить к нему, поэтому нам нужно его скопировать.)

Теперь

a = { a: { b: {c: 1 } } };                     // nested objects
notifySubobjectChanges(a);                     // set up recursive observers
Object.observe(a, console.log.bind(console));  // log changes to console
a.a.b.c = 99;

>> 0: Object
  name: "c"
  object: Object
  oldValue: 1
  path: "a.b"                                  // <=== here is your path!
  type: "update"

Приведенный выше код не является производственным, используйте его на свой страх и риск.

person Community    schedule 06.11.2014
comment
Хорошо, но как Object.observe() ведет себя именно в той ситуации, которую вы описали? Если два других объекта содержат первый объект, произойдет ли 3 события изменения? Новый объект передается обратному вызову, поэтому наследование/вложенность объектов не будет проблемой. Кстати: path не должно содержать movall, ob или ob2, а только следующее. В сочетании с переданным объектом изменением type и oldValue можно было бы напрямую создать операцию OT. - person CodeManX; 06.11.2014
comment
События изменения происходят только в том случае, если за ними наблюдают, подобно падению дерева в лесу. Таким образом, если бы мы также наблюдали ob2, то да, этому наблюдателю было бы отправлено другое событие изменения. - person ; 06.11.2014
comment
Просто для уточнения: если мы наблюдаем movall и ob2, ob2 содержит movall, и я делаю изменение в ob2ob2.b.movall.moo = "foo" — будет ли одно событие изменения в movall и одно в ob2? Если да, то нет неоднозначности пути, поскольку он будет относиться к объекту, который мы наблюдаем, независимо от вложенных объектов (moo для movall и b.movall.moo для ob2). - person CodeManX; 06.11.2014
comment
Если я правильно понял ваш комментарий, то без дополнительных механизмов изменение ob2.movall.moo не является изменением ob2. Изменения сообщаются только об объекте, содержащем свойство. Изменение ob2.movall.moo не является изменением какого-либо свойства ob2, это изменение внутреннего значения одного из его свойств. - person ; 06.11.2014
comment
Я вижу, и после того же, хотя это имеет смысл, и мне не нужны никакие дополнительные механизмы для получения событий для ob2 в этом случае. Тем не менее, я не понимаю, как тогда может быть неоднозначность пути в событии изменения к обратному вызову наблюдателя movall. - person CodeManX; 06.11.2014
comment
Ваш код не работает в Chrome Canary, процессор будет загружаться на полную мощность, а память съедается все больше (до гигабайт) - бесконечная рекурсия / одно событие вызывает другое в пинг-понговой усадьбе? (как с флагом гармонии, так и без него). Firefox + polyfill ничего не делает, даже не выдает ошибку. - person CodeManX; 06.11.2014
comment
Извините, посмотрите сейчас. Это был бесконечный цикл на событии modify. - person ; 06.11.2014
comment
Спасибо, теперь в принципе работает. Несколько замечаний: неатомарные типы внутри массивов не наблюдаются (массив внутри массива, объект внутри массива), я предполагаю, что массивы нужно обрабатывать по-другому (в настоящее время вы их игнорируете). Наблюдаются только пути, которые существовали до вызова notifySubobjectChanges(), если свойства добавляются позже, нужно будет либо повторно запустить эту функцию и добавить проверку безопасности, чтобы не наблюдать один и тот же объект во второй раз, либо автоматически наблюдать новые свойства в том виде, в каком они есть. добавлено (перехват в уже существующем обработчике наблюдения при добавлении события). - person CodeManX; 07.11.2014
comment
@CoDEmanX, спасибо за отзыв. Не совсем уверен, какие проблемы могут быть с массивами, так как это просто объекты со свойствами, к сожалению, сейчас нет времени на это смотреть. Да, вам потребуется дополнительная логика для обработки добавленных свойств, что не обязательно будет так просто. - person ; 07.11.2014