So, Object.observe
will become a standard at some point. In the meanwhile, you may want to unobtrusively log any methods called on an object from the outside. So I wrote this little snippet that helps me do just that.
Object.monitor = function(obj, quiet){ var keys = (function(obj){ // include from prototype also, any function. var keys = [], key; for (key in obj) typeof obj[key] === 'function' && keys.push(key); return keys; }(obj)), // alternating foreground colours for odd/even calls. colours = [ '#FFFFFF', '#CCCCCC' ], token = '%c', monitor = ' monitor:', log = function(what, method){ // more use goes red in console. console.log(token + '%d' + token + monitor + '%s', 'font-family:courier;background-color:rgb(' + Math.min(255, what) + ',0,0);color:' + colours[+(what %2 == 0)], what, 'font-family:courier', method ); }, counters = {}; keys.forEach(function(key){ var orig = obj[key]; Object.defineProperty(obj, key, { get: function(){ key in counters || (counters[key] = 0); counters[key]++; quiet || log(counters[key], key); return orig; } }); }); return function(){ // serialize the calls and order by counts return { raw: counters, sorted: keys.map(function(key){ return { key: key, value: counters[key] }; }).filter(function(obj){ return !!obj.value; }).sort(function(a, b){ return a.value <= b.value; }).map(function(obj){ var o = {}; return o[obj.value] = obj.key, o; }) } } };
Basically, saving a reference of the original method, logging and returning the method. Downside: just asking for a reference or toSource does not guarantee the method is being called. If you need to take that into account, simply wrap the method into a function instead.
Object.monitor
will return a reference to a function that you can call at any time to inspect the current results.
To use it, simply go:
var foo = { count: 0, one: function(){ this.count++; }, two: function(){ this.count += 2; } }; var fooStats = Object.monitor(foo); foo.one(); foo.two(); var c = 150; while(c--) foo.one(); console.log(fooStats()); // returns an object with sorted and raw counters.
See it in action: http://jsfiddle.net/dimitar/aCcVX/
Tweaks you can do to get different methods:
// only local keys not proto keys = Object.keys(obj).filter(... fn only ...); // local and prototype keys on a class keys = Object.keys(obj).concat(Object.keys(obj.constructor.prototype)).filter(... fn only ...);
The quiet
argument will prevent it from spamming your console. You can tweak to use console.count as well if you prefer.