At first I thought it was just assigning obj[0], obj[1], obj[2], etc. to the jQuery object before returning it, and that the length is manually assigned. But no, since console.log logs an array and not an object.
I took a quick look at the jQuery source but since I'm not familiar with it I didn't crack it easily. jQuery.makeArray
popped first, but it turned out to be the opposite of what I'm looking for, you actually lose the object methods by using it.
My first guess is initiating the array first, and then copying all the properties and methods from the object to it.
Does anybody with the jQuery source code experience have a clear answer to this?
At first I thought it was just assigning obj[0], obj[1], obj[2], etc. to the jQuery object before returning it, and that the length is manually assigned. But no, since console.log logs an array and not an object.
I took a quick look at the jQuery source but since I'm not familiar with it I didn't crack it easily. jQuery.makeArray
popped first, but it turned out to be the opposite of what I'm looking for, you actually lose the object methods by using it.
My first guess is initiating the array first, and then copying all the properties and methods from the object to it.
Does anybody with the jQuery source code experience have a clear answer to this?
Share Improve this question edited Oct 4, 2011 at 16:59 BoltClock 724k165 gold badges1.4k silver badges1.4k bronze badges asked Oct 4, 2011 at 16:57 trezniktreznik 8,12413 gold badges48 silver badges59 bronze badges 2-
It's supposedly similar to how
NodeList
works like an array but isn't. – BoltClock Commented Oct 4, 2011 at 16:59 - What Array-Objects? How do you know it logs an array and not an object? – Šime Vidas Commented Oct 4, 2011 at 17:03
3 Answers
Reset to default 13jQuery creates what are called (in the ES standard) array-like objects. In particular, they have a length property, and also (which makes use of the length property) they have the relevant items placed under integer-based indices.
It's as simple as:
var arrayLike = {length: 3, 0:'first', 1:'second', 2:'third'};
Array.prototype.join.call(arrayLike, '|'); // "first|second|third"
If you take a look at the standard (15.4.4), particularly the Array.prototype
methods you'll see the following note under each and every one:
NOTE The * function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method. Whether the * function can be applied successfully to a host object is implementation-dependent.
If you look at the description of these algorithms, they pretty much just use the length property to iterate over the object (in our case an array-like object) and access the values behind those integer-based keys. For that reason they're generic and work on regular js objects with the relevant properties.
Essentially that's all jQuery does (if I remember correctly the building is done in sizzle, jquery's css selector engine). You can test this out/prove it in a variety of ways. For example, a standard js Array performs some magic when the length property is shortened, but the jquery object doesn't:
var arr = [1,2,3], $arr = $('div'); // assuming three divs
arr.length=2; arr[2]; // undefined
$arr.length=2; $arr[2]; // still references the div
http://jsfiddle/cnkB9/
So jquery's makeArray
converts the jquery array-like object, and makes it into an actual native js array, but as you said, then it doesn't have all the jquery methods attached.
As for why it shows up in the console, I refer to this excellent answer: What makes Firebug/Chrome console treat a custom object as an array? that explains that the presence of the length
property and splice
function allow it to appear like an array in most consoles. As is mentioned, that isn't the case in FF4's Web Console, which just shows you that it's not a native js array. Interestingly enough, the splice
function doesn't actually have to work, just be present, and be a function. For example:
>> console.log({length:2,0:'some',1:'thing',splice:new Function});
["some", "thing"]
It's as simple as creating a new function and assigning it a new array as its prototype.
function ArrayLike() {}
ArrayLike.prototype = [];
So for example:
function ArrayLike() {}
ArrayLike.prototype = [];
ArrayLike.prototype.fromArray = function(arr) {
for(var i = 0; i < arr.length; i++)
this.push(arr[i]);
};
ArrayLike.prototype.foo = function() {
console.log("foo", this);
};
var a = new ArrayLike();
a.fromArray([1, 2]);
console.log(a);
a.foo();
http://jsfiddle/Xeon06/fUgaf/
The following simple code of how to transform an array returned by a jQuery selector in operation with a native JavaScript array:
var $arr = $('option'); // assuming three options
var arr = Array.apply(null, $arr);
// arr contains [option, option]
Considering the following HTML for the code above:
<html>
<head>
<script type="text/javascript" src="jquery-1.5.2.min.js"></script>
</head>
<body>
<select id="addSelection">
<option value="Original Value">Original Value</option>
<option value="Two Value">Value two</option>
<option value="Three Value">Value three</option>
</select>
<br />
<input id="btn" type="button" value="Changes" />
</body>
</hmtl>