A friend of mine gave me a great challenge. Find out how jQuery did to return objects that behave like arrays but that are not arrays! The aim of this article is to find out how Firebug and Web Inspector can display an object with the bracket notation.
$('a') >> [<a href="http://vjeux.com/">vjeux</a>, <a href="http://google.com/">google</a>] |
Not an Array!
First, let's make sure that this is really not an Array. If you wonder why just not subclassing the array, there are many reasons explained on this great article.
// Easy way typeof $('a') >> "object" // Normal way $('a') instanceof Array >> false // Duck typing ... $('a').indexOf >> undefined // Object toString // http://whattheheadsaid.com/2010/10/cross-context-isarray-and-internet-explorer Object.prototype.toString.call($('a')) >> "[object Object]" // http://ajaxian.com/archives/isarray-why-is-it-so-bloody-hard-to-get-right $('a').constructor >> function Object() { [native code] } // ES5 Way // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/isArray Array.isArray($('a')) >> false |
Testing ...
Obviously Firebug and Web Inspector must use another technique. What they need to display an Array is only the length
property and the keys [0 .. length-1]
. So let's try!
({0: 42, 1: 666, length: 2}) >> Object { 0=42, 1=666, length=2 } |
This is obviously not working 🙁
In Source the Truth Lies!
Hopefully, both Webkit and Firebug are open source projects, this means that anyone can browse the source code and therefore tell exactly what happens behind the scene.
// Web Inspector Source Code // http://trac.webkit.org/browser/trunk/WebCore/inspector/front-end/InjectedScript.js#L409 // FireBug's array detection. if (isFinite(obj.length) && typeof obj.splice === "function") return "array"; |
We can see that Duck Typing is being used. It is now really easy to trick Firebug and Web Inspector to make them believe that your objects are arrays! Only 3 small rules are required:
- [0 .. length - 1]: Array elements
- length: Set to a positive integer
- splice: Any function (even empty)
And here is the demo 🙂
({0: 42, 1: 666, length: 2, splice: function() {}}) >> [42, 666] |