Search code examples
javascriptgoogle-chromejavascript-objectsecmascript-harmonyobject.observe

Overwriting property in Object.observe()


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.


Solution

  • 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.