Array.from & Array.of
Array.from() and Array.of() are the two static factory methods introduced in ES2015 for building arrays. Array.from() converts anything iterable or array-like — a Set, a NodeList, a string, even arguments — into a real array, optionally mapping each element along the way. Array.of() exists to fix a long-standing quirk in the Array() constructor. Together they give you predictable, expressive ways to create arrays from sources that aren’t already arrays.
Array.from with iterables
Any value that implements the iterable protocol can be passed straight to Array.from() to produce a shallow-copied array. This is the cleanest way to turn a Set, Map, or string into something you can map, filter, or sort.
const unique = new Set([1, 1, 2, 3, 3]);
const list = Array.from(unique);
console.log(list);
console.log(Array.from("hello"));
Output:
[ 1, 2, 3 ]
[ 'h', 'e', 'l', 'l', 'o' ]
Because strings are iterable, Array.from() splits them by code point rather than UTF-16 code unit, so it handles emoji and other astral characters correctly where String.prototype.split("") would break them apart.
console.log(Array.from("a🎉b"));
console.log("a🎉b".split(""));
Output:
[ 'a', '🎉', 'b' ]
[ 'a', '\ud83c', '\udf89', 'b' ]
Array.from with array-likes
An array-like object has a numeric length property and indexed keys (0, 1, 2, …) but lacks array methods. Classic examples are the arguments object inside a function and DOM collections. Array.from() reads the length and copies each index into a genuine array.
function collect() {
return Array.from(arguments);
}
console.log(collect("a", "b", "c"));
console.log(Array.from({ length: 3, 0: "x", 1: "y", 2: "z" }));
Output:
[ 'a', 'b', 'c' ]
[ 'x', 'y', 'z' ]
Tip: The spread operator (
[...value]) also converts iterables to arrays and is shorter. But spread only works on iterables — it throws on plain array-like objects that lackSymbol.iterator.Array.from()handles both iterables and array-likes, so reach for it when the source is anarguments-style object.
The mapping function
Array.from() accepts an optional second argument: a map function applied to every element as the array is built. This is more efficient than Array.from(x).map(fn) because it avoids creating an intermediate array. A third argument sets this for the callback.
const doubled = Array.from([1, 2, 3], (n) => n * 2);
const squares = Array.from(new Set([2, 4, 6]), (n) => n ** 2);
console.log(doubled);
console.log(squares);
Output:
[ 2, 4, 6 ]
[ 4, 16, 36 ]
The callback receives the value and its index, exactly like Array.map().
Generating ranges and sequences
Combining the length array-like trick with the mapping function gives you a concise way to generate sequences without a manual loop. The classic idiom builds an array of a fixed size and fills it from each index.
// 0..4
const range = Array.from({ length: 5 }, (_, i) => i);
// 1..5
const oneToFive = Array.from({ length: 5 }, (_, i) => i + 1);
// 10, 20, 30
const tens = Array.from({ length: 3 }, (_, i) => (i + 1) * 10);
console.log(range);
console.log(oneToFive);
console.log(tens);
Output:
[ 0, 1, 2, 3, 4 ]
[ 1, 2, 3, 4, 5 ]
[ 10, 20, 30 ]
This pattern is the idiomatic JavaScript equivalent of a range() function from other languages.
Converting DOM NodeLists
In the browser, methods like document.querySelectorAll() return a NodeList, which is array-like (and in modern browsers, iterable) but only has a forEach. To use map, filter, or reduce, convert it first.
const items = document.querySelectorAll("li.todo");
const texts = Array.from(items, (li) => li.textContent.trim());
const completed = Array.from(items).filter((li) =>
li.classList.contains("done"),
);
console.log(texts);
console.log(completed.length);
The inline map function above grabs the text in a single pass, while the second example converts first and then filters.
Array.of versus Array()
The Array() constructor is ambiguous: called with a single number it creates an empty array of that length rather than an array containing that number. Array.of() was added to remove this footgun — it always treats its arguments as elements.
| Call | Result | Why |
|---|---|---|
Array(3) | [ <3 empty items> ] | Single number = length |
Array.of(3) | [ 3 ] | Number is an element |
Array(1, 2, 3) | [ 1, 2, 3 ] | Multiple args = elements |
Array.of(1, 2, 3) | [ 1, 2, 3 ] | Same here |
console.log(Array(7));
console.log(Array.of(7));
console.log(Array.of(1, 2, 3));
Output:
[ <7 empty items> ]
[ 7 ]
[ 1, 2, 3 ]
Gotcha: A sparse array from
Array(7)haslength7 but no actual elements, soArray(7).map(fn)does nothing —mapskips empty slots. UseArray.from({ length: 7 }, fn)to generate filled arrays instead.
Best Practices
- Use
Array.from()to convert array-like objects (arguments, olderNodeLists) where the spread operator would throw. - Prefer the spread operator (
[...iterable]) for plain iterables likeSetandMapwhen you don’t need a map function — it’s more concise. - Pass the mapping function as the second argument to
Array.from()instead of chaining.map(), to avoid an extra intermediate array. - Reach for
Array.from({ length: n }, (_, i) => ...)to generate ranges and filled sequences. - Use
Array.of()(or a literal) instead ofnew Array(n)when an argument might be a single number, to avoid accidentally creating a sparse array. - Remember both methods produce a shallow copy — nested objects are shared by reference, not cloned.