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]
If you liked this article, you might be interested in my Twitter feed as well.
 
 

Related Posts

  • August 19, 2011 Javascript – Stupid Idea: Hoisting at the end (133)
    JSLint imposes us to do manual hoisting of variables. What if we did it but at the end of the function? :P How you write function print_array (array) { var length = array.length; for (var i = 0; i < length; ++i) { var elem = array[i]; console.log(elem); […]
  • September 25, 2011 Javascript Object Difference (6)
    This article is about a difference algorithm. It extracts changes from one version of an object to another. It helps storing a smaller amount of information. Template In a project, I have a template object with all the default settings for a widget. var template = { […]
  • October 12, 2011 Intercept and alter <script> include (3)
    For a project, I want to transparently be able to intercept all the included javascript files, edit the AST and eval it. This way I can manipulate all the code of an application just by inserting a custom script. Hook the <script> tag insertion. Download the Javascript file […]
  • August 27, 2011 Start a technical blog, it’s worth it! (6)
    Lately, I've been advocating to all my student friends to start a blog. Here's an article with the most common questions answered :) What are the benefits? Being known as an expert. The majority of my blog posts are about advanced Javascript topics. As a result, I'm being tagged as […]
  • November 5, 2011 Simulated Annealing Project (2)
    For a school project, I have to implement Simulated Annealing meta heuristic. Thanks to many open source web tools, I've been able to quickly do the project and have a pretty display. CoffeeScript, Raphael, Highcharts, Three.js, Twitter Bootstrap, jQuery and Web […]