Moshe's Blog

Tips and tricks learnt along the way.

Object.observe Nested Objects

| Comments

A great article came out yesterday: Data-binding Revolutions with Object.observe()

If you didn’t read it yet, go read it then come back here.

Ok the basic idea of that article is that you can do things like this

var model = {};

// Which we then observe
Object.observe(model, function(changes){

    // This asynchronous callback runs and aggregates changes
    changes.forEach(function(change) {

        // Letting us know what changed
        console.log(change.type, change.name, change.oldValue);
    });

});
model.adding = 'a prop';
model.nested = { obj: 'thing' };
model.nested.obj = 'another thing';

running that in the console we get:

add adding undefined
add nested undefined

Now it would be cool if there was a way to observe nested objects. Here’s a simple function to do so:

function observeNested(obj, callback) {
    Object.observe(obj, function(changes){
        changes.forEach(function(change) {
            if (typeof obj[change.name] == 'object') {
                observeNested(obj[change.name], callback);
            }
        });
        callback.apply(this, arguments);
    });
}

Now at first glance it would seem this doesn’t work:

function observeNested(obj, callback) {
    Object.observe(obj, function(changes){
        changes.forEach(function(change) {
            if (typeof obj[change.name] == 'object') {
                observeNested(obj[change.name], callback);
            }
        });
        callback.apply(this, arguments);
    });
}

var model = {};

// Which we then observe
observeNested(model, function(changes){
    changes.forEach(function(change) {
        console.log(change.type, change.name, change.oldValue);
    });
});
model.adding = 'a prop';
model.nested = { obj: 'thing' };
model.nested.obj = 'another thing';

Still shows:

add adding undefined
add nested undefined

But then I remembered that (and I’m quiting from the article): Object.observe(), part of a future ECMAScript standard, is a method for asynchronously observing changes to JavaScript objects

So in the case above since the two assignments to model.nested happen right after another, it only logs it as one event, If you were using promises to some other async mechanism it would work as advertised:

function observeNested(obj, callback) {
    Object.observe(obj, function(changes){
        changes.forEach(function(change) {
            if (typeof obj[change.name] == 'object') {
                observeNested(obj[change.name], callback);
            }
        });
        callback.apply(this, arguments);
    });
}

var model = {};

// Which we then observe
observeNested(model, function(changes){
    changes.forEach(function(change) {
        console.log(change.type, change.name, change.oldValue);
    });
});
setTimeout(function() {
    model.adding = 'a prop';
}, 200);
setTimeout(function() {
    model.nested = { obj: 'thing' };
}, 400);
setTimeout(function() {
    model.nested.obj = 'another thing';
}, 600);

Which shows:

add adding undefined
add nested undefined
update obj thing

Comments