There is no one convention for looping in general, because there are different solutions for different situations.
Looping over an array
When it comes to looping over an array (or array-like objects such as jQuery objects and Argument objects) however, then there really is only one good way, and that's a for loop with a number counter. And the reason is that you want to loop over the the indexes, not over all properties unconditionally.
Example of dump for a jQuery object ($('#content');
):
{
selector: '#content',
context: HTMLDocument.
length: 1,
0: HTMLDivElement
}
/// inherits from jQuery.prototype
/// inherits from Object.prototype
You wouldn't want to loop over all properties, only the indices (based on the length). Even a hasOwnProperty filter won't help you here, because they are all "own" properties.
The other case is when there is a sparse array that may contain gaps, in most cases the intent is to loop over all of them, and yield undefined
for gaps. Another case is that in JavaScript everything is either a primitive (string, number, boolean, ..) or an object. Arrays are objects. And as such, keys of properties are strings, which can cause issues when accessed as such. A for-in loop will give you the internal key values, which are strings (as they should be).
e.g. the following will never log the "Success!" message:
var key,
arr = ['foo', 'bar'];
for ( key in arr ) {
console.log( 'Key:', $.toJSON( key ), 'Value:', $.toJSON( arr[key] ) );
if ( key === 1 ) {
console.log( 'Success! Found the second item (should be "bar"):' + arr[key] );
}
}
Because arr (as being an object) is { "0": 'foo', "1": 'bar' }
, and "1"
!== 1
.
Looping over an object
If you do want to loop over all properties of an object then, of course, there is nothing wrong with a for-in loop.
var obj = {
'foo': 'Hello Foolifications'
'bar': 'Rabarber Barbara\'s bar'
};
for ( key in arr ) {
console.log( 'Key:', $.toJSON( key ), 'Value:', $.toJSON( arr[key] ) );
if ( key === 1 ) {
console.log( 'Success! Found the second item (should be "bar"):' + arr[key] );
}
}
If you explicitly need to exclude inherited properties (e.g. you have an instance of FooBar that inherits from FooBar.prototype, and you want to clone it):
var clone, key,
hasOwn = Object.prototype.hasOwnProperty,
foo = new AwesomeFoo( /* .. */ );
/* .. */
clone = Object.create(AwesomeFoo.prototype);
for ( key in foo ) {
if ( hasOwn.call( foo, key ) {
clone[key] = foo[key];
}
}
So, what about $.each
? Well, $.each
is basically the same as a for-in loop ($.each does not filter hasOwn), with the difference that it has its own scope. In most cases you probably don't need it, but if you need to do a lot of loops and want to avoid conflicts with the different counter or key variables, then using a scope can be helpful. Especially in nested loops.