Array iteration and holes in JavaScript
The blog post describes how various functions and methods that deal with arrays are affected by holes [1].
All of the code below assumes that the following variable declaration has been made:
Above, we have created an array that has the element 'a' at index 0, a hole (no element at all) at index 1 and the element 'b' at index 2.
For this blog post, it is important to keep in mind that JavaScript ignores trailing commas in arrays. That means that you need to write two commas to create a trailing hole:
We use slice() to create a copy arr2 of an existing array arr:
The right-hand side of the above assignment is equivalent to:
forEach() skips holes:
every() also skips holes (similarly: some()):
map() skips, but preserves holes:
filter() eliminates holes:
join() converts holes and undefineds to empty strings.
All other array methods preserve holes. For example, sort():
The for loop does not access arrays, the for-in loop correctly lists property keys:
apply() treats holes as undefined elements. That allows you to easily create an array with a given number of undefineds [1]:
apply() nicely plugs holes of empty arrays. You cannot, however use it to do so for arbitrary arrays, which may or may not contain holes. For example, the arbitrary array [2] does not contain holes, so apply() should return it “unchanged”. But it doesn’t, it creates an empty array whose length is 2 (because the function Array() interprets single numbers as array lengths, not as array elements).
We have seen that JavaScript handles holes in a variety of ways. Thankfully, you normally don’t need to know how holes are handled, because arrays with holes should be avoided. Holes affect performance negatively and are rarely useful.
Running example
All of the code below assumes that the following variable declaration has been made:
var arr = [ 'a',, 'b' ];
Above, we have created an array that has the element 'a' at index 0, a hole (no element at all) at index 1 and the element 'b' at index 2.
Preliminaries
Trailing holes
For this blog post, it is important to keep in mind that JavaScript ignores trailing commas in arrays. That means that you need to write two commas to create a trailing hole:
> [ 'a', 'b', ]
[ 'a', 'b' ]
> [ 'a', 'b',, ]
[ 'a', 'b', , ]
Copying arrays
We use slice() to create a copy arr2 of an existing array arr:
var arr2 = arr.slice();
The right-hand side of the above assignment is equivalent to:
arr.slice(0, arr.length)
Array methods
forEach() skips holes:
> arr.forEach(function (x,i) { console.log(i+'.'+x) })
0.a
2.b
every() also skips holes (similarly: some()):
> arr.every(function (x) { return x.length === 1 })
true
map() skips, but preserves holes:
> arr.map(function (x,i) { return i+'.'+x })
[ '0.a', , '2.b' ]
filter() eliminates holes:
> arr.filter(function (x) { return true })
[ 'a', 'b' ]
join() converts holes and undefineds to empty strings.
> arr.join('-')
'a--b'
> [ 'a', undefined, 'b' ].join('-')
'a--b'
Other array methods
All other array methods preserve holes. For example, sort():
> var arr2 = arr.slice()
> arr2.sort()
[ 'a', 'b', , ]
Loops
The for loop does not access arrays, the for-in loop correctly lists property keys:
> var arr2 = arr.slice()
> arr2.foo = 123;
> for(var key in arr2) { console.log(key) }
0
2
foo
Function.prototype.apply()
apply() treats holes as undefined elements. That allows you to easily create an array with a given number of undefineds [1]:
> Array.apply(null, Array(3))
[ undefined, undefined, undefined ]
apply() nicely plugs holes of empty arrays. You cannot, however use it to do so for arbitrary arrays, which may or may not contain holes. For example, the arbitrary array [2] does not contain holes, so apply() should return it “unchanged”. But it doesn’t, it creates an empty array whose length is 2 (because the function Array() interprets single numbers as array lengths, not as array elements).
> Array.apply(null, [2])
[ , ,]
Conclusion and recommendation
We have seen that JavaScript handles holes in a variety of ways. Thankfully, you normally don’t need to know how holes are handled, because arrays with holes should be avoided. Holes affect performance negatively and are rarely useful.
Further reading
- “JavaScript: sparse arrays vs. dense arrays”
What are holes and how do they differ from elements that are undefined?
- “Iterating over arrays and objects in JavaScript”
General information on iterating over arrays.
Comments
Post a Comment