The changes array of an Object.observe()
callback contains objects with the following four properties:
Why isn't there a path
provided natively? Example:
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"
There's a Node.js module that extends Object.observe()
to also include the path: observed.js,
but I worry the performance gain of a native observe()
will be lost (if no, could you please explain how it is implemented then?). It might be possible to browserify the module, but can't imagine it will perform well in a synchronous environment and I still wonder why nobody seems to have thought about an additional path
property.
Because there is no clear path.
Consider the following:
var movall = {moo: "bar", val: 5};
var ob1 = {a: mooval};
var ob2 = {b: movall};
Now let's say I observe movall
. Then I update moo
. What is the path? Is it movall.moo
, or ob1.a.moo
, or ob2.b.moo
? If I observe ob1
, there is no change reported, since there is no change to any of its properties (the change was internal to one of its properties, which doesn't count).
Objects are independent of their existence nested within other objects. They can be nested within multiple other objects. There is no unique "path" that describes how to get from potentially multiple starting points down to a specific property which may have changed.
Nor does JS know the path by which you reached the property being changed. So in ob.foo[0].val = 1;
, JS simply evaluates the chain, arrives at the foo[0]
object, changes its val
property, and at that point has no idea how it happened to arrive at foo[0]
. All it knows is that foo[0]
has changed. It changed within ob
, but it might also have changed within some other object that happens to have foo[0]
as a property.
However, you can possibly achieve what you seem to be trying to by building some machinery on top of the low-level observe/notify mechanism. We shall define a function on an object which sets up observers on its property objects, and so on recursively, and propagates change records back up with properly constructed paths:
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
}
}
(Note: the change
object is frozen and we cannot add anything to it, so we have to copy it.)
Now
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"
The above code is not production-quality, use at your own risk.