Array.from()

ES6+

Creates a new Array instance from an array-like or iterable object.

Syntax

Array.from(arrayLike, mapFn, thisArg)

Parameters

arrayLike ArrayLike | Iterable

An array-like or iterable object to convert to an array

mapFn Function optional

Map function to call on every element of the array

Return Value

Array

A new Array instance

Examples

JavaScript
console.log(Array.from('hello'));
console.log(Array.from([1, 2, 3], x => x * 2));
Output:
// ['h', 'e', 'l', 'l', 'o'] [2, 4, 6]

📌 When to Use

Use Array.from() at the boundaries where "almost arrays" need to become real arrays: NodeList from querySelectorAll(), the arguments object in legacy functions, strings (split into characters), Sets after deduplication, and Map entries heading into array processing. It accepts both iterables (anything with Symbol.iterator) and array-likes (anything with a length and indexed properties), which is precisely what the spread operator does not: [...obj] requires an iterator, so spreading a plain {length: 3} object throws while Array.from({length: 3}) happily produces three undefined slots. That length-object trick powers its second major role, generation: Array.from({length: n}, (_, i) => expr) manufactures ranges, sequences, test fixtures, and - crucially - arrays of DISTINCT objects, the correct alternative to fill()'s shared-reference behavior. The optional mapping function is more than sugar: it transforms during construction in a single pass, avoiding the intermediate array that Array.from(src).map(fn) would allocate, and for strings it iterates by code points so emoji and other astral characters survive intact (unlike split("")). Prefer the terser [...x] for straightforward iterable-to-array conversion in modern code; reach for Array.from() when the source is merely array-like, when you want the built-in mapping pass, or when generating from a length.

⚠️ Common Mistakes

Assuming Array.from() and spread are interchangeable in both directions. For iterables they are; for array-LIKES they are not - [...arguments-style-object] throws "is not iterable" where Array.from() succeeds. Conversely, using the verbose Array.from(set) where [...set] reads cleaner is mere noise. Know which capability each actually has before choosing on style.

Writing Array.from(source).map(fn) and paying for two arrays. The second parameter IS a map: Array.from(source, fn) converts and transforms in one pass with one allocation. The chained version is not wrong, just wasteful - and on a 10,000-node NodeList the discarded intermediate is real garbage-collector work.

Confusing Array.from({length: n}) with Array(n). The constructor form creates n HOLES that map() and forEach() skip - Array(3).map(() => 1) stays stubbornly empty - while Array.from gives n real undefined elements that every method visits. When a generated sequence mysteriously comes out empty, this distinction is almost always the culprit.

Expecting a deep or even shallow-copy guarantee on nested content: Array.from(matrix) copies only the outer level, so the new array shares row references with the source - sorting the copy is safe, but editing copy[0][0] edits the original's row. Map each row through slice() for two-level independence: Array.from(matrix, row => row.slice()).

Passing an async mapping function: Array.from(urls, async u => fetch(u)) yields an array of PROMISES, not results - Array.from cannot await. Follow it with await Promise.all(...), or in newer runtimes use Array.fromAsync(), which was designed for exactly this.

✅ Best Practices

Memorize the range recipe Array.from({length: n}, (_, i) => i) and its offset/step variants ((_, i) => start + i * step) - JavaScript has no built-in range(), and this expression is the standard substitute for numbered lists, pagination controls, calendar grids, and test data.

Fold DOM extraction into the conversion: Array.from(document.querySelectorAll("a"), a => a.href) walks the NodeList once and lands directly on the data you wanted. Converting first and chaining map() afterward doubles the passes for identical output.

Generate arrays of independent objects with a factory callback - Array.from({length: 3}, () => ({votes: 0})) runs the factory per slot, making it the safe counterpart to fill({}), whose single shared object is a notorious trap. Any per-slot randomness or ids belong here too.

Convert strings with Array.from(str) rather than str.split("") whenever the text can contain emoji or non-BMP characters: from() iterates by code points, so "👍" stays one element instead of shattering into two broken surrogate halves.

Dedupe-and-transform in one expression by combining with Set: Array.from(new Set(emails), e => e.toLowerCase()) - the Set collapses duplicates, and the mapping argument normalizes each survivor during materialization.

⚡ Performance Notes

Array.from() is O(n) in the source size, and its cost profile depends on the path taken: for true arrays and common iterables V8 has fast paths that make it comparable to spread, while the generic iterator-protocol path (calling next() per element) is inherently slower than a raw memcpy-style clone - one reason [...arr] can edge out Array.from(arr) in microbenchmarks, though both are far from bottleneck territory in application code. The mapping-argument form is a genuine optimization over convert-then-map, halving allocations by fusing the two passes. For pure generation, Array.from({length: n}, fn) invokes fn n times; when the value is a constant, new Array(n).fill(v) skips the callbacks entirely and wins clearly at scale. The generated arrays come out dense and packed, giving subsequent operations V8's best element-kind representations. Practical guidance: worry about which construction idiom at the million-element scale or in per-frame loops; below that, choose by clarity - the conversion is a one-time cost dwarfed by whatever you do with the array afterwards.

🌍 Real World Example

Converting DOM NodeList and Generating Sequences

Four everyday faces of Array.from() in one place. The NodeList conversion is the DOM-scripting staple - querySelectorAll() results lack map() and filter(), and the mapping argument extracts each button's label during the single conversion pass. The range and alphabet generators both exploit the {length: n} array-like: no source data exists at all, just a count and a formula per index, which is how JavaScript compensates for having no range() builtin (the alphabet works by offsetting from char code 65, "A"). Last, the string conversion splits "Hello" into characters by code points - the safe method when input might contain emoji, where the older split("") approach corrupts surrogate pairs.

// Convert NodeList to array (enables array methods)
const buttons = document.querySelectorAll('button');
const buttonTexts = Array.from(buttons, btn => btn.textContent);

// Generate a range of numbers [1, 2, 3, 4, 5]
const range = Array.from({length: 5}, (_, i) => i + 1);

// Generate alphabet array
const alphabet = Array.from({length: 26}, (_, i) =>
  String.fromCharCode(65 + i)
);
// ['A', 'B', 'C', ..., 'Z']

// Convert string to array of characters
const chars = Array.from('Hello');
// ['H', 'e', 'l', 'l', 'o']

Related Methods