The article discusses the ES6 spread operator in JavaScript, cautioning against its use due to performance issues that were present at the time of writing, despite its benefits for code readability and maintainability.
Abstract
The ES6 spread operator in JavaScript is a syntactic feature that allows for more concise and readable code, particularly when dealing with functions and arrays. Initially, the author embraced the spread operator for its ability to simplify code in a new project, Clio, a modern programming language that compiles to JavaScript. However, performance benchmarks revealed significant drops when using the spread operator, especially in function calls and array manipulations. The author conducted tests comparing the spread operator with traditional methods, such as using the apply method and concat for arrays, which consistently showed better performance. Despite the cleaner code syntax, the author concluded that the performance penalties incurred by the spread operator were too great and thus avoided its use in performance-critical parts of the Clio language, such as the exception and traceback system.
Opinions
The spread operator, while making code shorter and more readable, was found to cause substantial performance issues in JavaScript.
The author initially favored the spread operator for its modern syntax and code simplification benefits.
After encountering performance bottlenecks in the Clio project, the author emphasizes the importance of performance over syntactic convenience.
The author suggests that there are alternative ways to maintain code readability without compromising performance.
The article implies that developers should be cautious when adopting new syntax features and should always consider the performance implications of their choices.
What is the ES6 spread operator and why you shouldn’t use it
Important note: This article is no longer accurate, these performance issues are fixed in the recent versions of V8.
JavaScript introduced a spread operator in ES6, which helps you write shorter and nicer code. I started using this operator everywhere I could immediately after learning about it, because why not? It’s the new syntax that was introduced, it’s the future, it simplifies many things, it makes your code shorter while maintaining readability, it’s perfect.
Recently I started working on a new project named Clio. It’s a new and modern programming language made for the cloud. It’s functional, and it compiles to JavaScript. While writing the lexer, the parser and the code generator for this new language, I encountered numerous difficulties and problems caused by JavaScript performance and limitations.
Clio is a functional language and it should be fast, so after writing each new part of its code, I did several performance checks and benchmarks. After coding the exception and the traceback system of Clio, I noticed a huge performance drop in my tests. After investigating the issue, I realized it is caused simply by using the spread operator in my code.
Let’s analyze some of the most common uses of the spread operator.
Functions and function calls
Let’s say you have a list of numbers and you want to pass the items in that list to a function as arguments. There’s always the good old way, defining a function that doesn’t have any arguments, using the special arguments variable and using apply to pass our list of numbers to this function.
That looks a little complicated and hackish. The alternative way is the ES6 way of doing it. You define your function and use the spread operator to say you’re expecting many arguments, then you again use the spread operator to pass your array to this new fancy function.
This is really, really nice. It makes the code shorter, more readable and easier to maintain. But what are the costs? I wrote a few tests on jsperf to test the performance of each method against the other ones. Here are the results:
The blue bar is when the function is defined without using the spread operator and called without using the spread operator. The red bar is when we define the function without the spread operator but call it using the spread operator.
The orange bar is when we define the function using the spread operator, and we use the apply method to pass our arguments. The green bar is when we define the function using the spread operator and passe the arguments using the spread operator.
As you can see, using the spread operator for defining functions doesn’t make that much difference, but when passing arguments the spread operator causes a huge performance drop.
Arrays
One more common use of the spread operator is to expand, unpack or merge arrays together. Imagine you have two lists of numbers and you want to make a list that has all items from these two lists. Of course, you can use a loop to iterate over all items and add them to a new list, but there is also the concat method of JavaScript Arrays that we can use. And again, the new fancy way of doing this is to use the spread operator:
Again, it looks nice, shorter and more maintainable. It’s cleaner and less complicated compared to the old method of doing it. But what about performance? Here is a jsperf test to measure the performance of a few different ways of doing this:
The blue bar shows the results of using the concat method. The red bar for using two for loops to iterate over the two lists and push them to another one. Finally, the orange one uses the spread operator.
Conclusion
While the spread operator is really really nice, and while it helps to make the code more readable and maintainable, I refuse to use it when possible. In my opinion, it isn’t worth it to have a piece of code that is 10x slower, but readable. I do, of course, care about the readability of my code, but there are other ways of maintaining readability without hurting code performance.
In conclusion, I avoided using the spread operator in the Clio exception and traceback system.