In this second installment we are still looking inside the jQuery code.
Trust me, even if it's hard to digest, you can still learn enough
if you focus on a little bit at a time.
The code we are interested in today is the following.
//from jQuery 1.3.2
get: function( num ) {
return num === undefined ?
// Return a 'clean' array
Array.prototype.slice.call( this ) :
// Return just the object
this[ num ];
},
That's the code for the command jQuery.fn.get(index),
which returns the element at the given index or the entire array if the index is
omitted.
The JavaScript idiom that is really interesting in this function is that
strangely long function call Array.prototype.slice.call( this ).
That line is needed because we need to return an array and, even though they look like one, the
jQuery objects aren't arrays. And they aren't alone in that.
If it walks like a duck...
The Array object is one that we can't avoid becoming
familiar with in JavaScript. They are everywhere. Data is passed
to functions as arrays. Data is returned in arrays.
Or are they?
Aside from jQuery objects there are at least two other important occurrences
of data structures that are used like arrays but really aren't: the
arguments
variable and the DOM NodeList
collections.
The arguments variable is the list of parameters passed to
the current function and the NodeList is what is returned from members of the the DOM API such as
document.getElementsByTagName() or element.childNodes.
Both of these types have a length property and expose their
items with indices, like arguments[1] or elements[0].
But don't let this small coincidence fool you. As soon as you stop paying attention
and try to use another array method, like push, shift,
join you'll have your dreams shattered and a
TypeErrorto handle.
Help me, jQuery
To solve the above problem and return a real array object instead of the jQuery
object itself, the code used the technique we highlighted.
Array.prototype.slice.call( this )
The idea is to use the
array.slice()
instance method
to create a new array. The slice method returns a chunk of the array and it
takes two optional parameters (the boundaries) that, when omitted, make the function return
a copy of the array itself.
I've written about the
prototype
object and
how to invoke functions with call
in previous posts, so I won't
repeat myself here. I'll just add a reminder that the this object in the get
method is the jQuery instance we're working with.
What's going on in call( this ) is that slice is
being invoked as if it was a method of the jQuery object (note: the jQuery object
does have a method called slice but it
does not return an array.)
You may be wondering how can we pass a jQuery object to
slice.call() and not get any errors inside the method implementation. Well,
it works because the slice method
was built to be generic and it
only needs that the current object has a length property and items
in indexed positions. That's Duck
Typing at work.
A Freebie
There's a small variation of this technique that you may come across in JavaScript
as well.
[].slice.call( this )
It calls the same slice method but in a tad more wasteful manner
because a new Array instance is created each time just to provide access to its slice method.
Well, that's it. I hope you don't scratch your head any longer when you see
an expression like XYZ.prototype.someMethod.call(obj) from now on.
Posted
12-22-2009 11:13 PM
by
sergiopereira