32

I'm wondering how jQuery constructs its array-like object. The key thing I'm trying to work out is how it manages to get the console to interpret it as an array and display it as such. I know it has something to do with the length property, but after playing a bit I can't quite figure it out.

I know this has no technical advantage over a normal array like object as in the example below. But I think it's an important semantic element when users are testing and debugging.

A normal Array like Object.

function foo(){
    // Array like objects have a length property and it's properties use integer
    // based sequential key names, e.g. 0,1,2,3,4,5,6 just like an array.
    this.length = 1;
    this[0] = 'hello'
}
// Just to make sure add the length property to the prototype to match the Array 
// prototype
foo.prototype.length = 0;

// Give the Array like object an Array method to test that it works     
foo.prototype.push = Array.prototype.push

// Create an Array like object 
var bar = new foo;

//test it 
bar.push('world');

console.log(bar);
// outputs 
{ 0: 'hello',
  1: 'world',
  length: 2,
  __proto__: foo
}

Where as jQuery would output

var jQArray = $('div')

console.log(jQArray);

// outputs
[<div></div>,<div></div>,<div></div>,<div></div>]

If you run

console.dir(jQArray)

// Outputs

{ 0: HTMLDivElement,
  1: HTMLDivElement,
  2: HTMLDivElement,
  3: HTMLDivElement,
  4: HTMLDivElement,
  context: HTMLDocument,
  length: 5,
  __proto__: Object[0]
 }

The proto of the jQuery object is especially interesting since its the Object and not jQuery.fn.init as would be expected, also the [0] indicates something as this is what you get when you.

console.dir([])
// outputs Array[0] as the object name or Array[x] x being the internal length of the
// Array

I have no idea how jQuery has set it's proto to be Object[0] but my guess is that answer lies somewhere in there. Anyone got any ideas?

Ry-
  • 218,210
  • 55
  • 464
  • 476
AshHeskes
  • 2,304
  • 1
  • 21
  • 27
  • I'm not sure about this, but couldn't you create your object then set its prototype to Array.prototype? It will then be an array-like object? Or no? – John Strickler Jul 06 '11 at 15:39
  • well as far as I know that will make it a normal array. But I also want to avoid that since I don't want to add all the Array methods to my object, in order to avoid confusion. As some Array methods will actually return a new Array, so when a user uses one of those methods all the other methods that where attached to my array like object will be lost. – AshHeskes Jul 06 '11 at 15:47

2 Answers2

43

The object has to have length and splice

> var x = {length:2, '0':'foo', '1':'bar', splice:function(){}}
> console.log(x);
['foo', 'bar']

and FYI, the Object[0] as the prototype is for exactly the same reason. The browser is seeing the prototype itself as an array because:

$.prototype.length == 0;
$.prototype.splice == [].splice;
  • 1
    No need for splice if you don't need the object to print as an array with console.log(). length and properties like 0,1 will suffice to use the array methods. – daremkd Jan 21 '16 at 15:24
  • @zyklus Do you happen to know if this is part of the ECMA spec (array-like objects) or is this a browser nicety? – Usagi Dec 13 '16 at 21:45
  • @Usagi - AFAIK this has nothing to do with ECMA script and is just a random dev tools thing. There are a lot of "array-like" objects in JS, both in the language and user-created, so it's useful to show them as arrays if they implement some minimum subset of `Array`, this is just what the devs picked. –  Dec 14 '16 at 01:38
0

Like this?

function foo() {
  this.push('hello');
}
foo.prototype = [];

var bar = new foo();
console.log(bar.length); // 1
console.log(bar); // ["hello"]
Andrey M.
  • 3,688
  • 3
  • 33
  • 36
  • That has the same effect as the response I gave @JohnStrickler. I don't want to add all the Array methods to my Object to avoid confusion as some array methods actually return a new Array. So any other methods I attach to the object will be lost, when they are used. – AshHeskes Jul 06 '11 at 15:53
  • jQuery object uses Array as its prototype. I think you can redefine this methods so they will return new foo object instead of regular array. – Andrey M. Jul 06 '11 at 16:00
  • 5
    @Andrey M. - Please don't go saying stuff that you don't know for sure. jQuery does ___not___ use `Array` as it's prototype. It uses `push`, `sort`, and `splice` from `Array`, but that's it. –  Jul 06 '11 at 16:04
  • 2
    I took another look up the prototype Chain of the jQuery object and cannot find Array defined anywhere within it. – AshHeskes Jul 06 '11 at 16:05