Avoid reading outside of collection bounds#407
Avoid reading outside of collection bounds#407mathiasbynens wants to merge 2 commits intojquery:mainfrom
Conversation
|
Hey. This looks nice to me. How this reflects on the byte size though? Maybe you could provide a small jsperf as well? |
| // Fall back on getElementsByName | ||
| elems = context.getElementsByName( id ); | ||
| length = elems.length; | ||
| i = 0; |
There was a problem hiding this comment.
This could moved to the body of a for right?
There was a problem hiding this comment.
Yes, for both i and length. However, i was already outside of the for, so I decided to follow the existing style.
There was a problem hiding this comment.
Oh, that because of the https://contribute.jquery.org/style-guide/js/#good-examples, i.e. it should be fine to put i to the body, as long its initialised on top of the scope, like we doing here – https://github.com/jquery/jquery/blob/692f9d4db30c9c6c4f6bc76005cf153586202fa6/src/effects.js#L614 :)
|
|
||
| // Fall back on getElementsByName | ||
| elems = context.getElementsByName( id ); | ||
| length = elems.length; |
There was a problem hiding this comment.
NodeList#length can be an element in IE<9 (and we should have a test where that is the case on this line). cf. f6e2fc5#diff-7d34356c0b7229dd5da8b6c7711b32b7R1733
There was a problem hiding this comment.
So, something like this?
if (typeof elem.length === 'number') {
fastPath();
} else {
slowPathForLegacyBrowsers();
}There was a problem hiding this comment.
Yes, provided we can get it without excessive file size increase.
There was a problem hiding this comment.
for ( ; i !== len && (elem = elems[i]) != null; i++ ) {| // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too | ||
| results = context.getElementsByTagName( tag ); | ||
| results = context.getElementsByTagName( tag ), | ||
| length = results.length; |
| j = 0, | ||
| i = 0; | ||
| i = 0, | ||
| length = results.length; |
Consider the following collection:
const array = ['a', 'b', 'c'];
Retrieving `array[0]` can be done relatively quickly. However, when the property
doesn’t exist on the receiver, JavaScript engines must continue to look up the
prototype chain until either the property is found or the chain ends. This is
inherently slower than *not* doing any prototype chain lookups. Retrieving an
out-of-bounds index, e.g. `array[3]`, triggers this scenario, resulting in
decreased performance.
This patch changes the way some loops are written to avoid running into the slow
case unnecessarily.
88e5e6d to
893e3c5
Compare
|
This is pretty big... can you put together a jsPerf or other benchmark (example, and yes I'm aware of the irony) showing the value? |
| length = elems.length; | ||
| i = 0; | ||
| while ( (elem = elems[i++]) ) { | ||
| for ( ; i !== length && (elem = elems[ i ]) != null; i++ ) { |
There was a problem hiding this comment.
This is probably not an issue, but I feel like i < length is safer.
There was a problem hiding this comment.
i < length would definitely be an issue, because length might be a DOM node in IE<9.
There was a problem hiding this comment.
Perhaps we could put in a comment relating that as a reminder.
|
If the Chrome perf quirk was the main driver for this, that has been fixed. |
|
Consider the following collection:
Retrieving
array[0]can be done relatively quickly. However, when the property doesn’t exist on the receiver, JavaScript engines must continue to look up the prototype chain until either the property is found or the chain ends. This is inherently slower than not doing any prototype chain lookups. Retrieving an out-of-bounds index, e.g.array[3], triggers this scenario, resulting in decreased performance.This patch changes the way some loops are written to avoid running into the slow case unnecessarily.
Similar patch for jQuery: jquery/jquery#3769