Array
methods
Array.from(arrayLike, mapFunc?, thisArg?)
Array.of(...items)
Array.prototype
methods
Array.prototype.copyWithin()
Array.prototype.fill()
undefined
elementsconcat()
(Symbol.isConcatSpreadable
)
Symbol.isConcatSpreadable
in the standard libraryNew static Array
methods:
Array.from(arrayLike, mapFunc?, thisArg?)
Array.of(...items)
New Array.prototype
methods:
Array.prototype.entries()
Array.prototype.keys()
Array.prototype.values()
Array.prototype.find(predicate, thisArg?)
Array.prototype.findIndex(predicate, thisArg?)
Array.prototype.copyWithin(target, start, end=this.length)
Array.prototype.fill(value, start=0, end=this.length)
Array
methods The object Array
has new methods.
Array.from(arrayLike, mapFunc?, thisArg?)
Array.from()
’s basic functionality is to convert two kinds of values to Arrays:
length
and indexed elements. Examples include the results of DOM operations such as document.getElementsByClassName()
.Map
and Set
.The following is an example of converting an Array-like object to an Array:
Array.from()
Array.from()
is also a convenient alternative to using map()
generically:
In this example, the result of document.querySelectorAll()
is again an Array-like object, not an Array, which is why we couldn’t invoke map()
on it. Previously, we converted the Array-like object to an Array in order to call forEach()
. Here, we skipped that intermediate step via a generic method call and via the two-parameter version of Array.from()
.
from()
in subclasses of Array Another use case for Array.from()
is to convert an Array-like or iterable value to an instance of a subclass of Array
. For example, if you create a subclass MyArray
of Array
and want to convert such an object to an instance of MyArray
, 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 its sub-constructors).
You can also combine this functionality with mapping, to get a map operation where you control the result’s constructor:
The species pattern lets you configure what instances non-static built-in methods (such as slice()
, filter()
and map()
) return. It is explained in Sect. “The species pattern” in Chap. “Classes”.
Array.of(...items)
Array.of(item_0, item_1, ···)
creates an Array whose elements are item_0
, item_1
, etc.
Array.of()
as an Array literal for subclasses of Array
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):
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()
).
Array.prototype
methods Several new methods are available for Array instances.
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:
I could also have used the spread operator (...
) to convert iterators to Arrays:
[index, element]
pairs You can combine entries()
with ECMAScript 6’s for-of
loop and destructuring to conveniently iterate over [index, element]
pairs:
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:
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:
The full signature of the callback predicate
is:
NaN
via findIndex()
A well-known limitation of Array.prototype.indexOf()
is that it can’t find NaN
, because it searches for elements via ===
:
With findIndex()
, you can use Object.is()
(explained in the chapter on OOP) and will have no such problem:
You can also adopt a more general approach, by creating a helper function elemIs()
:
Array.prototype.copyWithin()
The signature of this method is:
It copies the elements whose indices are in the range [start
,end
) to index target
and subsequent indices. If the two index ranges overlap, care is taken that all source elements are copied before they are overwritten.
Example:
Array.prototype.fill()
The signature of this method is:
It fills an Array with the given value
:
Optionally, you can restrict where the filling starts and ends:
Holes are indices “inside” an Array that have no associated element. In other words: An Array arr
is said to have a hole at index i
if:
i
< arr.length
!(i in arr)
For example: The following Array has a hole at index 1.
You’ll see lots of examples involving holes in this section. Should anything ever be unclear, you can consult Sect. “Holes in Arrays” in “Speaking JavaScript” for more information.
undefined
elements The general rule for Array methods that are new in ES6 is: each hole is treated as if it were the element undefined
. Examples:
The idea is to steer people away from holes and to simplify long-term. Unfortunately that means that things are even more inconsistent now.
The iterator created by Array.prototype[Symbol.iterator]
treats each hole as if it were the element undefined
. Take, for example, the following iterator iter
:
If we invoke next()
twice, we get the hole at index 0 and the element 'a'
at index 1. As you can see, the former produces undefined
:
Among others, two operations are based on the iteration protocol. Therefore, these operations also treat holes as undefined
elements.
First, the spread operator (...
):
Second, the for-of
loop:
Note that the Array prototype methods (filter()
etc.) do not use the iteration protocol.
Array.from()
If its argument is iterable, Array.from()
uses iteration to convert it to an Array. Then it works exactly like the spread operator:
But Array.from()
can also convert Array-like objects to Arrays. Then holes become undefined
, too:
With a second argument, Array.from()
works mostly like Array.prototype.map()
.
However, Array.from()
treats holes as undefined
:
Array.prototype.map()
skips them, but preserves them:
Array.prototype
methods In ECMAScript 5, behavior already varied slightly. For example:
forEach()
, filter()
, every()
and some()
ignore holes.map()
skips but preserves holes.join()
and toString()
treat holes as if they were undefined
elements, but interprets both null
and undefined
as empty strings.ECMAScript 6 adds new kinds of behaviors:
copyWithin()
creates holes when copying holes (i.e., it deletes elements if necessary).entries()
, keys()
, values()
treat each hole as if it was the element undefined
.find()
and findIndex()
do the same.fill()
doesn’t care whether there are elements at indices or not.The following table describes how Array.prototype
methods handle holes.
Method | Holes are | |
---|---|---|
concat |
Preserved | ['a',,'b'].concat(['c',,'d']) → ['a',,'b','c',,'d'] |
copyWithin ES6
|
Preserved | [,'a','b',,].copyWithin(2,0) → [,'a',,'a'] |
entries ES6
|
Elements | [...[,'a'].entries()] → [[0,undefined], [1,'a']] |
every |
Ignored | [,'a'].every(x => x==='a') → true |
fill ES6
|
Filled | new Array(3).fill('a') → ['a','a','a'] |
filter |
Removed | ['a',,'b'].filter(x => true) → ['a','b'] |
find ES6
|
Elements | [,'a'].find(x => true) → undefined |
findIndex ES6
|
Elements | [,'a'].findIndex(x => true) → 0 |
forEach |
Ignored | [,'a'].forEach((x,i) => log(i)); → 1 |
indexOf |
Ignored | [,'a'].indexOf(undefined) → -1 |
join |
Elements | [,'a',undefined,null].join('#') → '#a##' |
keys ES6
|
Elements | [...[,'a'].keys()] → [0,1] |
lastIndexOf |
Ignored | [,'a'].lastIndexOf(undefined) → -1 |
map |
Preserved | [,'a'].map(x => 1) → [,1] |
pop |
Elements | ['a',,].pop() → undefined |
push |
Preserved | new Array(1).push('a') → 2 |
reduce |
Ignored | ['#',,undefined].reduce((x,y)=>x+y) → '#undefined' |
reduceRight |
Ignored | ['#',,undefined].reduceRight((x,y)=>x+y) → 'undefined#' |
reverse |
Preserved | ['a',,'b'].reverse() → ['b',,'a'] |
shift |
Elements | [,'a'].shift() → undefined |
slice |
Preserved | [,'a'].slice(0,1) → [,] |
some |
Ignored | [,'a'].some(x => x !== 'a') → false |
sort |
Preserved | [,undefined,'a'].sort() → ['a',undefined,,] |
splice |
Preserved | ['a',,].splice(1,1) → [,] |
toString |
Elements | [,'a',undefined,null].toString() → ',a,,' |
unshift |
Preserved | [,'a'].unshift('b') → 3 |
values ES6
|
Elements | [...[,'a'].values()] → [undefined,'a'] |
Notes:
['a',,].length → 2
const log = console.log.bind(console);
Holes being treated as undefined
elements by the new ES6 operations helps with creating Arrays that are filled with values.
Array.prototype.fill()
replaces all Array elements (incl. holes) with a fixed value:
new Array(3)
creates an Array with three holes and fill()
replaces each hole with the value 7
.
Array.prototype.keys()
reports keys even if an Array only has holes. It returns an iterable, which you can convert to an Array via the spread operator:
The mapping function in the second parameter of Array.from()
is notified of holes. Therefore, you can use Array.from()
for more sophisticated filling:
undefined
If you need an Array that is filled with undefined
, you can use the fact that iteration (as triggered by the spread operator) converts holes to undefined
s:
The ES5 method filter()
lets you remove holes:
ES6 iteration (triggered via the spread operator) lets you convert holes to undefined
elements:
concat()
(Symbol.isConcatSpreadable
) You can configure how Array.prototype.concat()
treats objects by adding an (own or inherited) property whose key is the well-known symbol Symbol.isConcatSpreadable
and whose value is a boolean.
By default, Array.prototype.concat()
spreads Arrays into its result: their indexed elements become elements of the result:
With Symbol.isConcatSpreadable
, you can override the default and avoid spreading for Arrays:
For non-Arrays, the default is not to spread:
You can use Symbol.isConcatSpreadable
to force spreading:
How does concat()
determine if a parameter is an Array? It uses the same algorithm as Array.isArray()
. Whether or not Array.prototype
is in the prototype chain makes no difference for that algorithm. That is important, because, in ES5 and earlier, hacks were used to subclass Array
and those must continue to work (see the section on __proto__
in this book):
Symbol.isConcatSpreadable
in the standard library No object in the ES6 standard library has a property with the key Symbol.isConcatSpreadable
. This mechanism therefore exists purely for browser APIs and user code.
Consequences:
Array
are spread by default (because their instances are Array objects).Array
can prevent its instances from being spread by setting a property to false
whose key is Symbol.isConcatSpreadable
. That property can be a prototype property or an instance property.concat()
if property [Symbol.isConcatSpreadable]
is true
. That would enable one, for example, to turn on spreading for some Array-like DOM collections.concat()
, either.For Arrays, ES6 still has the same rules as ES5:
l
are in the range 0 ≤ l
≤ 232−1.i
are in the range 0 ≤ i
< 232−1.Strings and Typed Arrays have a larger range of indices: 0 ≤ i
< 253−1. The upper bound of that range is due to 253−1 being the largest integer that JavaScript’s floating point numbers can represent safely. For details, see Sect. “Safe integers”.
The only reason for the smaller index range of normal Arrays is backward compatibility.