This blog post explains what new array methods ECMAScript 6 will bring and how to use them in current browsers.
Note: I’m using the terms constructor and class interchangeably.
Class methods
Array
has gained methods of its own.
Array.from(arrayLike, mapFunc?, thisArg?)
Array.from()
’s basic functionality is to convert two kinds of objects to arrays:
- Array-like objects, which have a property
length
and indexed elements. Examples include the results of DOM operations such asdocument.getElementsByClassName()
. - Iterable objects, whose contents can be retrieved one element at a time. Arrays are iterable, as are ECMAScript’s new data structures
Map
andSet
.
The following code is an example of converting an array-like object to an array:
let lis = document.querySelectorAll('ul.fancy li'); Array.from(lis).forEach(function (li) { console.log(node); });
The result of querySelectorAll()
is not an array and does not have a forEach()
method, which is why we need to convert it to an array before we can use that method.
Array.from
allows you to control the constructor it uses for its result. For example, if you create a subclass MyArray
of Array
(subclassing arrays is explained in [1]) and want to convert something array-like or iterable to an instance of it, you simply use MyArray.from()
. The reason that that works is because constructors inherit from each other in ECMAScript 6 (a super-constructor is the prototype of a sub-constructor).
class MyArray extends Array { ... } let instanceOfMyArray = MyArray.from(anIterable);
Now we can see why the parameter mapFunc
is useful: it makes Array.from()
a variant of Array.prototype.map()
that produces a result whose constructor you can specify (via the constructor on which from()
is invoked):
let instanceOfMyArray = MyArray.from([1, 2, 3], x => x * x); let instanceOfArray = [1, 2, 3].map(x => x * x);
The (last) parameter of both methods is an arrow function.
Array.of(...items)
If you want to turn several values into an array, you should always use an array literal, especially since the array constructor doesn’t work properly if there is a single value that is a number (more information on this quirk):
> new Array(3, 11, 8) [ 3, 11, 8 ] > new Array(3) [ , , ,] > new Array(3.1) RangeError: Invalid array length
But how are you supposed to turn values into an instance of a sub-constructor of Array
then? This is where Array.of()
helps (remember that sub-constructors of Array
inherit all of Array
’s methods, including of()
).
class MyArray extends Array { ... } console.log(MyArray.of(3, 11, 8) instanceof MyArray); // true console.log(MyArray.of(3).length === 1); // true
Array.of()
is also handy as a function that doesn’t have Array()
’s quirk related to wrapping values in arrays. However, be careful about an Array.prototype.map()
pecularity that can trip you up here:
> ['a', 'b'].map(Array.of) [ [ 'a', 0, [ 'a', 'b' ] ], [ 'b', 1, [ 'a', 'b' ] ] ] > ['a', 'b'].map(x => Array.of(x)) // better [ [ 'a' ], [ 'b' ] ] > ['a', 'b'].map(x => [x]) // best (in this case) [ [ 'a' ], [ 'b' ] ]
As you can see above, map()
passes three parameters to its callback, the last two are simply often ignored (details).
Prototype methods
Several new methods are available for array instances.
Iterating over arrays
The following methods help with iterating over arrays:
Array.prototype.entries()
Array.prototype.keys()
Array.prototype.values()
The result of each of the aforementioned methods is a sequence of values, but they are not returned as an array; they are revealed one by one, via an iterator. Let’s look at an example (I’m using Array.from()
to put the iterators’ contents into arrays):
> Array.from([ 'a', 'b' ].keys()) [ 0, 1 ] > Array.from([ 'a', 'b' ].values()) [ 'a', 'b' ] > Array.from([ 'a', 'b' ].entries()) [ [ 0, 'a' ], [ 1, 'b' ] ]
You can combine entries()
with ECMAScript 6’s for-of
loop [2] and destructuring to conveniently iterate over (index, element) pairs:
for (let [index, elem] of ['a', 'b'].entries()) { console.log(index, elem); }
Note: this code already works in the current Firefox.
Searching for array elements
Array.prototype.find(predicate, thisArg?)
returns the first array element for which the callback predicate
returns true
. If there is no such element, it returns undefined
. Example:
> [6, -5, 8].find(x => x < 0) -5 > [6, 5, 8].find(x => x < 0) undefined
Array.prototype.findIndex(predicate, thisArg?)
returns the index of the first element for which the callback predicate
returns true
. If there is no such element, it returns -1
. Example:
> [6, -5, 8].findIndex(x => x < 0) 1 > [6, 5, 8].findIndex(x => x < 0) -1
Both find*
methods ignore holes [3]. The full signature of the callback predicate
is:
predicate(element, index, array)
Finding NaN
via findIndex()
A well-known limitation of Array.prototype.indexOf()
is that it can’t find NaN
, because it searches for elements via ===
:
> [NaN].indexOf(NaN) -1
With findIndex()
, you can use Object.is()
[4] and will have no such problem:
> [NaN].findIndex(y => Object.is(NaN, y)) 0
You can also adopt a more general approach, by creating a helper function elemIs()
:
> function elemIs(x) { return Object.is.bind(Object, x) } > [NaN].findIndex(elemIs(NaN)) 0
Array.prototype.fill(value, start?, end?)
Fills an array with the given value
:
> ['a', 'b', 'c'].fill(7) [ 7, 7, 7 ]
Holes [3] get no special treatment:
> new Array(3).fill(7) [ 7, 7, 7 ]
Optionally, you can restrict where the filling starts and ends:
> ['a', 'b', 'c'].fill(7, 1, 2) [ 'a', 7, 'c' ]
When can I use the new array methods?
- Some of them are already available in browsers. As usual, check kangax’s ECMAScript 6 compatibility table.
- Paul Miller’s es6-shim library has backported them to ECMAScript 5.
6 comments:
Good overview!
1. Instead of "constructor methods", how about the term "static Array functions"?
2. In section 1.2., could you define what Array.of() does? That section is a bit confusing without the definition.
Correction: "Static Array *methods*" is best, I think.
s/es5-shim/es6-shim/
Indeed. Fixed, thanks!
My current favorite is “class methods”.
Thanks for this nice article.
Exciting ES6 news! =]
Post a Comment