Array.from() vs spread syntax

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?

26671 次浏览

The difference is that spread allows an array to be expanded. Whereas from() creates a new array. .from() doesn't expand upon anything, it creates a new array based on the data provided; the spread operator on the other hand can expand an array with new properties.

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);

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.

Spread element (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 (i.e. objects that have the length property and indexed elements) which are not iterable. 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 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]).

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

However, based on the benchmarks, the spread operator seems to perform better 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")

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().