1

I want to use Chrome's experimental Object.observe() in order to overwrite all functions being set on the object:

→ jsFiddle

var obj = {};

Object.observe(obj, function (changes) {
    changes.forEach(function (data) {
        if ((data.type == "new" || data.type == "updated") &&
            typeof data.object[data.name] == "function" &&
            typeof data.object[data.name].isWrapper == "undefined") {

            data.object[data.name] = function () {
            };
            data.object[data.name].isWrapper = true;

        }

    });
});

obj.helloWorld = function () {
    console.log("helloWorld() was called");
};
obj.helloWorld();

Unfortunately, the console still displays "helloWorld() was called". Is it actually possible to overwrite the currently changed value in an object observer?

Since this is only an experiment (no production code!), I appreciate any kind of solution.

Bergi
  • 513,640
  • 108
  • 821
  • 1,164
ComFreek
  • 27,416
  • 16
  • 97
  • 150

1 Answers1

1

Well, you cannot really solve the problem at hand. While you can overwrite changed values again in the observer, the observer is only executed asynchronously, unless Object.deliverChangeRecords is explicitly called, so it is only executed after obj.helloWorld() was already called in the same turn it was defined.

I updated your fiddle to show just that:

var obj = {};

function obs(changes) {
    changes.forEach(function (data) {
        if ((data.type == "new" || data.type == "updated") &&
            typeof data.object[data.name] == "function" &&
            typeof data.object[data.name].isWrapper == "undefined") {

            data.object[data.name] = function () {
                console.log("intercepted", data.name);
            };
            data.object[data.name].isWrapper = true;

        }

    });
}

Object.observe(obj, obs);

obj.helloWorld = function () {
    console.log("helloWorld() was called");
};
// Will call the original function, as changes are not yet delivered.
obj.helloWorld();

Object.deliverChangeRecords(obs); 
// Will call the intercepted function, as the changes were explicitly delivered synchronously.
obj.helloWorld();

obj.helloWorld2 = function () {
    console.log("helloWorld2() was called");
};
// Will call the intercepted function, as first the changes will be delivered (end of turn) and only then the timeout callback will be called.
setTimeout(function() { obj.helloWorld2(); }, 0);

Not entirely sure if the setTimeout bits are implicitly mandated by the spec proposal or just an implementation detail, though.

Since there is no way to observe any changes immediately and synchronously without the modifying code explicitly executing Object.deliverChangeRecords, this API isn't really suited for what you're trying to achieve, at least when it comes to the current spec proposal.

A viable alternative to Object.observe might be Proxy, which is actually meant to do things like this and which IIRC is also available in Chrome (with experimental harmony features turned on). Here is a fiddle using Proxy.

nmaier
  • 29,828
  • 5
  • 59
  • 75
  • Many thanks for your answer! Regarding the term "current processing turn (or “Microtask”)" (found on your linked MDN page), do you have an official description of what a "microtask" is at hand? I only found some forum and mailing list posts which describe a specific "queue" containing the so-called microtasks. – ComFreek Nov 29 '13 at 20:16
  • 1
    Not really having an official description at hand (if there is any). In short it just means, that the changes won't be delivered until the currently running code is done running. So the normal behavior unless specified otherwise. For example if you run `var i; setTimeout(function() { i = 2000000; }, 0); for(i = 0; i < 100000; ++i);` the timeout callback won't be run before the current execution context (Draft 8.3) will be done, i.e. after the for loop is done. And if `i` was `Object.observe()d`, the observer won't be called until the for loop is done, as well. – nmaier Nov 29 '13 at 20:53
  • Damn, looked in the wrong place. See the [HTML5 spec](http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#event-loops)... – nmaier Nov 29 '13 at 20:58
  • Thanks for your research and explanation, I'll have a look at those links! You deserve your bounty (in 21 hrs.) ;) – ComFreek Nov 29 '13 at 21:02
  • Got curious if Chrome supports enough of Proxy, and it seems it does... well kinda. See updated answer. (I skipped implementing `fix`, though). – nmaier Nov 29 '13 at 21:33