There is no such thing as the Spread Operator in JavaScript!

georgegkas

George Gkasdrogkas

Posted on April 10, 2020

There is no such thing as the Spread Operator in JavaScript!

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?

  1. c1 has no value. The expression ...a1 will throw TypeError, because a1 is null.
  2. c1 is [1, 2]. The expression a1 || 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];
  1. c2 is [1, 2]. The expression a2 || b2 will be evaluated first, which will be spread.
  2. 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];
  1. c3 has no value. The expression ...a3 will throw TypeError, because a3 is null.
  2. c3 has no value. The expression a3 || b3 will evaluate first which will return null and then the Spread Syntax will throw TypeError.

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.

💖 💪 🙅 🚩
georgegkas
George Gkasdrogkas

Posted on April 10, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related