110

Is there some difference between using Array.from(document.querySelectorAll('div')) or [...document.querySelectorAll('div')]?

Here is a example:

let spreadDivArray = [...document.querySelectorAll('div')];
console.log(spreadDivArray);

let divArrayFrom = Array.from(document.querySelectorAll('div'));
console.log(divArrayFrom);

The console.log() will log the same result.

Is there any performance difference?

5
  • good thing with spread operator is that it supports Object. performance.. idk Commented Nov 11, 2016 at 12:40
  • To find out if there's any performance difference, run a benchmark. The results are likely to be quite different depending on whether you're in a native ES6 environment or transpiling to ES5. Commented Nov 11, 2016 at 12:46
  • 7
    The main difference is that Array.from works with array-like objects which don't implement the iterator protocol (i.e. Symbol.iterator). Even with ES6 and new browser specs, there are fewer and fewer of those. Commented Nov 11, 2016 at 12:46
  • 5
    ... is not an operator! Commented Nov 11, 2016 at 15:56
  • 1
    In addition to performance, there may be different upper limits in array size that these can handle. On Chrome at least, the spread operator seems to throw a "Maximum call stack size exceeded" when used with very large arrays, while Array.from() works fine. Commented May 13, 2022 at 7:47

7 Answers 7

105

Spread element or syntax (note that it's not an operator) works only with objects that are iterable (i.e. implement the @@iterator method). Array.from() works also on array-like objects which are not iterable (i.e. objects that have indexed elements and a length property).

See this example:

const arrayLikeObject = { 0: 'a', 1: 'b', length: 2 };

// This logs ['a', 'b']
console.log(Array.from(arrayLikeObject));

// This throws TypeError: arrayLikeObject[Symbol.iterator] is not a function
console.log([...arrayLikeObject]);

Also, if you just want to convert something to an array, I think it's better to use Array.from() because it's more readable. Spread elements are useful for example when you want to concatenate multiple arrays (['a', 'b', ...someArray, ...someOtherArray]).

Sign up to request clarification or add additional context in comments.

5 Comments

While I agree that Array.from() is an extremely readable way to implement this, I feel that the spread element syntax ...arrayLikeObject is just as readable for people (or more so).
Also note spread syntax (...arrayLikeObject) is much shorter. That can be a factor sometimes, though maybe it shouldn't be.
@qarthandso If we are spreading into the new (different) array, then I would agree. But if we need to duplicate an array (into exact same one), then Array.from looks more attractive and at least in some cases more readable, i.e when we need to pass a starting value of Array.prototype.reduce to be the array, on which we called it.
Warning: number arguments such as var i = 5; Array.from(i) results in [] where as var i = 5; [i] results in [5]
@Eduard "Array.from looks more attractive" what makes programming syntax attractive?
12

Well, Array.from is a static method, i.e., a function whereas the spread syntax is part of the array literal syntax. You can pass functions around like data, you can invoke them once, several times or not at all. This isn't possible with the spread syntax, which is static in this regard.

Another difference, which @nils has already pointed out, is that Array.from also works with array-like objects, which don't implement the iterable protocol. spread on the other hand requires iterables.

1 Comment

"Array.from also works with array-like objects, which don't implement the iterable protocol" -- can you give an example of one such object?
5

The difference is that spread ... allows an array to be expanded, whereas Array.from() creates a new array directly.

In your example, [...x] creates a new array by using spread syntax to expand the existing array values into the array literal syntax– the brackets [] are what's actually doing the array-creation bit. Those brackets can be swapped out with any number of other syntax to achieve other results (e.g. spreading into function parameters foo(...x), into an object {...x}, using multiple spreads to concatenate arrays [...x, ...y], etc.).

.from() never expands upon anything, it always creates a new array based on the data provided.

7 Comments

Well, array literals always create a new array as well…
I'm not sure whether I just misunderstand your wording, but are you suggesting that the spread operator mutates the array instead of creating a new one?
@Bergi well the spread operator doesn't create an array. The array creation in OP's example is done via the square brackets surrounding the spread operator.
Ah, OK, I just wanted to be sure you meant the right thing. Maybe it would help if you said "expands the array literal" instead of "expands an array", since it doesn't operate on arbitrary arrays.
To clarify: The ...foo syntax just spreads (expands) all array values as if they were separate, comma-separated arguments. The [] around it is what CREATES a new array. So [...foo] will create a new array and populate it by spreading all array elements as if they were array constructor arguments and does a FULL COPY of every element. Whereas Array.from(foo) will CREATE a new array using the input variable, and is A LOT FASTER because it creates a SHALLOW COPY (this is FASTER).
|
4

If the input is iterable they do the exact same thing.

Based on the benchmarks, the spread operator performs as good as Array.from for a Set.

https://jsben.ch/5lKjg

let set = new Set();
for (let i = 0; i < 10000; i++) {
  set.add(Math.random());
}


let tArrayFrom = window.performance.now()

let arr = Array.from(set)

console.log("Array.from():", window.performance.now() - tArrayFrom + "ms")


// slightly faster in most of the runs:
let tSpread = window.performance.now()

let arr2 = [...set];

console.log("Spread syntax:", window.performance.now() - tSpread + "ms")

2 Comments

On Chrome as of today, Array.from() seems slightly faster than spread now with this benchmark.
For me the spread syntax is faster by a couple ms
3

I forked the bench from @amin-ya and added some different sources.

Array.from is faster for each source.(sometimes marginally, sometimes significantly - so much variation, that i'm not sure we can trust jsben.ch) https://jsben.ch/hC1rE

tried https://jsbench.me/zxlvvg75ld/1 which also seems inconsistent between runs and between browsers... (results may vary)

enter image description here

2 Comments

You may want to post this as a comment on their answer
@Bergi, i think this warrants an own answer, as it actually shows (in 2024 chromium at least) that Array.from is faster across several sources.
2

Using Babel is a good way to see what's happening internally.

Heads up, though. Make sure latest is selected in Babel, as the default is wrong.

Using your example above, this is the output.

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

var spreadDivArray = [].concat(_toConsumableArray(document.querySelectorAll('div')));
console.log(spreadDivArray);

var divArrayFrom = Array.from(document.querySelectorAll('div'));
console.log(divArrayFrom);

7 Comments

[].concat doesn't appear to work if the node list is not concatspreadable? Is that a Babel output?
Is that a Babel output Indeed, I just copied the code to babeljs.io, have you an example?, maybe babel does other transformations when required. This of course is only doing testing for this specific case.
The babeljs.io repl has some weird options, it's not really reliable. Using [].concat is an incorrect simplification (does only do the same as spread syntax on array arguments), which might be caused by a bug in Babel or some unknown setting.
Ah, clicking latest in babel makes a difference.. I'll update answer.. with new output.. Thanks for the heads up.
Not to mention that you do not see "what happens internally" when you look at Babel transpiled code. What a runtime does internally is something else entirely.
|
-3

I need to clarify everyone's answers:

  • The ...foo syntax just spreads (expands) all array values as if they were separate, comma-separated arguments. It does a shallow spread. Any primities (numbers, strings etc) are COPIED and any complex values (objects) are instead REFERENCED.
  • The [] around it is what CREATES a new array.
  • So [...foo] will create a new array and populate it by doing a SHALLOW COPY spreading of all array elements as if they were array constructor arguments which in turn takes all those copied elements and puts them in the new array.
  • Whereas Array.from(foo) will CREATE a new array using the input variable, but is A LOT FASTER because it ONLY creates a SHALLOW COPY (this is FASTER). So it takes the exact input and just puts every variable/reference into the new array.
  • Use Array.from().

1 Comment

This is incorrect both the spread operator and array.from() creates shallow copies.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.