Detecting Object Mutations by Counting Properties

Have you ever included a library and wonder, "how much did this library add to the window object", or passed an object into a function and asked yourself, "did that function modify my object"? Instead of reading the source code, this article shows a quick trick for answering these questions.

How do it…

For starters we need a function to count the number of properties on an object:

function fnCountProperties(o) {
  var iCountPrototype = 0;
  var iCountOwn = 0;
  var iTotal = 0;

  for (var prop in o) {
    if (o.hasOwnProperty(prop)) {
      iCountOwn += 1;
    } else {
      iCountPrototype += 1;
    }
  }

  iTotal = new Number(iCountOwn + iCountPrototype);
  iTotal.countOwn = iCountOwn;
  iTotal.countPrototype = iCountPrototype;

  return iTotal;
}

To use, simply pass an object in before calling a function or including a library, and then pass the object in after and compare the difference:

function fnBlackBox(obj) { /* unknown */ };
var oMyObject = {
    test: 1
};
// will be 1
var iCountBefore = fnCountProperties(oMyObject);
fnBlackBox(oMyObject); // unknown operation
// if not 1, indicates that fnBlackBox modified the object
var iCountAfter = fnCountProperties(oMyObject);

Additionally, you can look at whether the property counts come from values assigned directly to the object or from the object’s prototype:

var iCount = fnCountProperties(oMyObject);
console.log('Total Count:' + iCount);
console.log('Number of direct properties:' + iCount.countOwn);
console.log('Number of prototype properties:' + iCount.countPrototype);

This breakdown is useful, because a property defined by the prototype can be modified, making it a direct property of the object. The total count will be the same, but the countOwn will increment and the countPrototype will decrement:

function Test() {};
Test.prototype = {
    state: 1
};
var oMyObject = new Test();
// iCount = 1, countOwn = 0, countPrototype = 1
var iCount = fnCountProperties(oMyObject);
oMyObject.state = 2;
// iCount = 1, countOwn = 1, countPrototype = 0
var iCount = fnCountProperties(oMyObject);

I have also created a little code pen to help illustrate:

See the Pen tIGHD by Matt Snider (@mattsnider) on CodePen.


How it works…

There is nothing fancy in the fnCountProperties function, it is using a for … in loop to iterate over the properties of an object. It then uses the hasOwnProperty method of Object to see if the property is directly assigned to the object or derived from the prototype. We create a Number instance to return the result, instead of using the primitive, so that we can assign countOwn and countPrototype to the returned value. This way developers can distinguish between whether the object or the prototype was modified.

To make this function useful, include it and run it on an object before you suspect the object is modified and compare the counts before with those after. While production ready, it is a testing tool, and you probably don’t need/want to include it in your production code. One of my favorite uses of this function is to include it early and run it before each library that I use to see how much each library modifies the window object. A well-written library won’t add more than a couple global variables.

Additionally, if you run it before including your own code, you can catch places where you did not expect the window object to be modified, and find that you forgot to include a var before defining variables.

Lastly, this technique is not full-proof, because (for performance) it doesn’t keep track of the property names. If a function removes a property and adds a new one, then the resulting counts before and after will be the same. But, since very few developers actually delete properties from objects, and since this is only meant as a developers tool, I believe this drawback is acceptable.