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.