6
var obj = {};
obj.a = 1; // fire event, property "a" added 

This question is different from this one, where ways to detect when an already declared property is changed, being discussed.

Community
  • 1
  • 1
Scholle
  • 1,519
  • 1
  • 20
  • 43
  • 1
    There's nothing in the current ECMAScript standard, but the next standard will likely implement a feature called `Proxy` that will offer this capability. Firefox already has this implemented. – I Hate Lazy Oct 01 '12 at 18:06
  • 1
    possible duplicate of [Detecting change in a Javascript Object](http://stackoverflow.com/questions/11578377/detecting-change-in-a-javascript-object) – Bergi Oct 01 '12 at 18:12
  • related: [Object.watch](http://stackoverflow.com/q/1029241/1048572) - only for selected properties – Bergi Oct 01 '12 at 18:18
  • @Bergi: Both posts refer to ways to detect property changes on a property of a given JavaScript object ALREADY declared. The lib (Watch.js or the newer one MultiGetSet.js) of the accepted answer allow you to watch a JavaScript object's properties by calling one method only (so it's a convenient thing here), but this lib does NOT consider "when you add new attributes for this object and/or change it, the watcher will not be invoked" (taken from Watch.js gist). I want to detect when new properties are added to a JavaScript object (detecting value changes of new properties comes second). – Scholle Oct 01 '12 at 18:28
  • @Scholle: No, [my answer](http://stackoverflow.com/a/11578545/1048572) on the duplicate deals with detecting any property changes on an object – Bergi Oct 01 '12 at 19:12
  • @Bergi, Ok, I focused on the accepted answer, sorry, but maybe there is a solution besides Proxy. – Scholle Oct 01 '12 at 19:25

3 Answers3

0

You could count the properties on the object and see if has changed from when you last checked:

How to efficiently count the number of keys/properties of an object in JavaScript?

this is a crude workaround, to use in case you can't find a proper support for the feature in the language.

Community
  • 1
  • 1
Nelson
  • 43,933
  • 8
  • 62
  • 77
  • I got your point, but how do you know WHEN to count the number of properties? Running a loop is not acceptable, especially not in a browser environment. – Scholle Oct 01 '12 at 18:42
  • a setInterval() with whichever time interval suits your needs. – Nelson Oct 01 '12 at 18:43
  • 1
    Sub optimal, you potentially may have lots of objects, setting up a setInterval() and running a function counting properties and optionally, determine which property has been added, several times a second on each of those, doesn't sound healthy. In addition, setInterval() is obviously not blocking in the browser, but execution of the function registered on setInterval() may be postponed. – Scholle Oct 01 '12 at 18:55
  • VOTED UP -- this is easily adapted to @Elias Van Ootegem's answer. Instead of a `for(var key in object)` loop, simply check `Object.keys(object).length`. WHY SHOULD @Nelson's answer be downvoted??? – Cody Sep 10 '14 at 20:11
  • delete obj.prop1; obj.prop2 = 'xyz'; count remains the same. – foxontherock Jul 27 '15 at 19:33
0

this is possible, technically, but since all current JS implementations that I know of are single threaded it won't be very elegant. The only thing I can think of is a brute force interval:

var checkObj = (function(watchObj)
{
    var initialMap = {},allProps = [],prop;
    for (prop in watchObj)
    {
        if (watchObj.hasOwnProperty(prop))
        {//make tracer object: basically clone it
            initialMap[prop] = watchObj[prop];
            allProps.push(prop);//keep an array mapper
        }
    }
    return function()
    {
        var currentProps = [];
        for (prop in watchObj)
        {
            if (watchObj.hasOwnProperty(prop))
            {//iterate the object again, compare
                if (watchObj[prop] !== initialMap[prop])
                {//type andvalue check!
                    console.log(initialMap[prop] + ' => ' watchObj[prop]);
                    //diff found, deal with it whichever way you see fit
                }
                currentProps.push(prop);
            }
        }
        //we're not done yet!
        if (currentProps.length < allProps.length)
        {
            console.log('some prop was deleted');
            //loop through arrays to find out which one
        }
    };
})(someObjectToTrack);
var watchInterval = setInterval(checkObj,100);//check every .1 seconds?

That allows you to track an object to some extent, but again, it's quite a lot of work to do this 10/sec. Who knows, maybe the object changes several times in between the intervals, too.
All in all, I feel as though this is a less-then-ideal approach... perhaps it would be easier to compare the string constants of the JSON.stringify'ed object, but that does mean missing out on functions, and (though I filtered them out in this example) prototype properties.

I have considered doing something similar at one point, but ended up just using my event handlers that changed the object in question to check for any changes.
Alternatively, you could also try creating a DOMElement, and attach an onchange listener to that... sadly, again, functions/methods might prove tricky to track, but at least it won't slow your script down as much as the code above will.

Elias Van Ootegem
  • 67,812
  • 9
  • 101
  • 138
0

If performance matters and you are in control of the code that changes the objects, create a control class that modifies your objects for you, e.g.

var myObj = new ObjectController({});

myObj.set('field', {});
myObj.set('field.arr', [{hello: true}]);
myObj.set('field.arr.0.hello', false);

var obj = myObj.get('field'); // obj === {field: {arr: [{hello: false}]}}

In your set() method, you now have the ability to see where every change occurs in a pretty high-performance fashion, compared with setting an interval and doing regular scans to check for changes.

I do something similar but highly optimised in ForerunnerDB. When you do CRUD operations on the database, change events are fired for specific field paths, allowing data-bound views to be updated when their underlying data changes.

Rob Evans
  • 6,066
  • 3
  • 35
  • 52