Have you heard about the Spread Syntax? Introduced in ES2015, we love it due to its simple semantics and ubiquitous use cases. What about the Spread Operator? Yes, it's the three dots (...
) used by the Spread Syntax!
…and by saying such things, we start digging into the horrifying world of a bug's life…
A quick review of the Spread Syntax
A simple use case where we can leverage the use of the Spread Syntax is when we want to concatenate multiple arrays. Check the following snippet:
const clientErrors = ['err1', 'err2', 'err3'];
const serverErrors = ['err4', 'err5'];
function numberOfErrors(clientErrors, serverErrors) {
// Assuming that both inputs are arrays to prevent TypeErrors.
return [...clientErrors, ...serverErrors].length;
}
numberOfErrors(clientErrors, serverErrors); // => 5
The function numberOfErrors
concatenates two arrays and returns the length of the new array. But what if the parameters are falsy values, like null
or undefined
?
const clientErrors = ['err1', 'err2', 'err3'];
const serverErrors = null;
function numberOfErrors(clientErrors, serverErrors) {
return [...clientErrors, ...serverErrors].length;
}
numberOfErrors(clientErrors, serverErrors);
// => TypeError
We know that if we try to spread a null
or undefined
variable, this will make the interpreter nag. In real world scenarios, we want to guard ourselves from such edge cases. With a minor tweak, we end up writing something like this:
const clientErrors = ['err1', 'err2', 'err3'];
const serverErrors = null
function numberOfErrors(clientErrors, serverErrors) {
return [...(clientErrors || []), ...(serverErrors || [])].length;
}
numberOfErrors(clientErrors, serverErrors) // => 3
Because serverErrors
is falsy, the logical OR operator will return an empty array, which then will be spread gracefully. The final result from calling numberOfErrors
is equal to the length of the clientErrors
array, which is 3
.
Spread Operator Precedence
Now that we covered a basic example, let's see something more interesting. For each of the following questions, mark the correct answer. The solutions will be presented immediately after. (Hint: You can run the code snippets and see the results yourself!)
Question A
const a1 = null;
const b1 = [1, 2];
const c1 = [...a1 || b1];
What is the value of c1
?
-
c1
has no value. The expression...a1
will throwTypeError
, becausea1
isnull
. -
c1
is[1, 2]
. The expressiona1 || b1
will be evaluated first, which then will return[1, 2]
, which will be spread.
Question B
const a2 = [1, 2];
const b2 = null;
const c2 = [...a2 || b2];
-
c2
is[1, 2]
. The expressiona2 || b2
will be evaluated first, which will be spread. -
c2
is[1, 2]
. The expression…a2
will be evaluated first, which will be spread.
Question C
const a3 = null;
const b3 = null;
const c3 = [...a || b];
-
c3
has no value. The expression...a3
will throwTypeError
, becausea3
isnull
. -
c3
has no value. The expressiona3 || b3
will evaluate first which will returnnull
and then the Spread Syntax will throwTypeError
.
Answers
A. 2
B. 1
C. 2
If it happens that you have not answered correctly to at least one of the above questions, then you might have fallen into the trap of the operator precedence. Does the dots punctuator …
have higher precedence over the logical OR ||
, or is the other way around? What is the precedence of the Spread Operator? The correct answers is: It does not matter, because there is no such thing as Spread Operator in JavaScript!
The Spread Operator does not exist!
When we try to evaluate expressions like […array || []]
it's logical to examine the precedence of our operators. There exist a common misconception in the Web regarding the Spread Syntax, which is presented as an operator.
A great answer was posted in Stack Overflow by Andrew Li, which is worth mentioning and summarizes the nature of the Spread Syntax.
One of the most memorable arguments can be retrieved directly by the ECMAScript 2015 specification itself:
The complete list of operators is listed in Clauses §12.5 through §12.15 in the ECMAScript 2015 Language Specification, the specification in which
…
is introduced, which doesn't mention...
. - Andrew Li answer
Another worth-mentioning point is that "An operator is a builtin function [..] that **evaluates to exactly one value.". If we try to run a statement like const a = …b
in our Web Console, where b
is an array, then we'll SyntaxError
.
The way that the Spread Syntax works, is by evaluating its arguments first, and then spreading the result. Thus, […a || b]
behaves exactly the same way as […(a || b)]
. Putting a set of parentheses around a || b
expression helps to remove the ambiguity.
As a practical reference, Spread Syntax's arguments are evaluated first and then spread.
Top comments (0)