Question

Why would the Sizzle selector engine use push.apply( results.... ) over results.push(...) it seems unnecessary to me. Can someone explain the motivation?

To elaborate, I've become interested in writing/borrowing bits from sizzle for a lighter weight selector engine. I figure I don't need some things like :contains(text) which would reduce the weight even further. So reading through the source I see

var arr = [],
    push = arr.push
    results = results || [];
 ....
 push.apply( results, context.getElementsByTagName( selector ) );

The code makes sense, except wouldn't it be simpler to use

results.push( context.getElementsByTagName( selector ) );

I don't intend to be naggy about such a minor convention, I just want to know if I'm missing something like a context issue.

Was it helpful?

Solution

It is instead of:

results.concat(array)

Because concat creates an extra array, but push.apply won't:

push.apply(results, array)

The results array is cached and no extra arrays are created.

But you could also do:

results.push.apply(results, array)

I'm not sure why the need for arr.

Edit:

I'm thinking the need for the extra arr might be to convert the pseudo-array that getElementsByTagName returns into a real array.

OTHER TIPS

Looking over the code again (after taking a break). Around line 205, Sizzle checks if the selector pattern is an ID and uses results.push

elem = context.getElementById( m );
results.push( elem );
return results;

Line 237 onwards is for Elements or Classes and uses getElementsByTagName or getElementsByClassName along with push.apply( results, ... ).

I assume its a short hand version of

for( elem in context.getElementsByClassName( m ) ) {
    results.push( elem );
}

As is the case in the Mozzila docs example https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply

// short hand
var max = Math.max.apply(null, numbers);
var min = Math.min.apply(null, numbers);

/* vs. simple loop based algorithm */
max = -Infinity, min = +Infinity;

for (var i = 0; i < numbers.length; i++) {
  if (numbers[i] > max)
    max = numbers[i];
  if (numbers[i] < min) 
    min = numbers[i];
}

EDIT:

From my original question results.push( context.getElementsByTagName( selector ) ); would result in an unwanted Object. This pushes the one argument of type NodeList into results.

Example:

var a = [1, 2, 3], b = [], c =[];
b.push( a ); // b.length = 1, now we have a multidimensional array
[].push.apply( c, a ); // c.length = 3, we now have a clean array, not a NodeList
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top