What is the purpose of prefixing parameters with '...'?

What is the purpose of prefixing parameters with '...'?
0.0 0

#1

I recently did a coding challenge in checkio.org and had a bit of trouble with the Math.max and min functions. I eventually passed it using Math.max.apply(arguments).
When I looked at the top voted solution I noticed that they simply prefixed the parameter with ‘…’ in both the function definition and as the argument for Math.max etc and bypassed the need to use apply(). What exactly do those three dots do?
My solution:

"use strict";

function mostNumbers(numbers) {
    return numbers ? Math.max.apply(null, arguments) - Math.min.apply(null, arguments) : 0;

}
Math.round(mostNumbers(10.2, -2.2, 0, 1.1, 0.5) * 1000) // 12400

top solution:

"use strict";


function mostNumbers(...args){

    return args.length === 0 ? 0 : Math.max(...args) - Math.min(...args)

}

#2

The one in the parameter list is a “rest parameter”. Multiple parameters are packed into one named array - in this case args.

The two down in return statement are spread operator. That unpacks args as a list of comma separated values (Math.max can take multiple parameters)

function func(...args){
  console.log(args);  // [12,13,14,15]
  console.log(...args); // 12,13,14,15
}

func(12,13,14,15);

#3

That’s a spread operator, one of the newcomers introduced with ES6.

Pretty much allows you to expand in place an iterable.

It’s a really useful feature that I tend to use more and more often during every day coding.

From function:

var arr = [2, 4, 8, 6, 0];
var max = Math.max(...arr); // spread the array instead of manually calling each one

// equivalent to Math.max.apply(null, arr);

To copying objects and array:

const a = [1,2,3];
const b = [...a, 4]; // [1,2,3,4]

const o ={
  a: [1,2,3],
  b: { c: "d"},
  bool: true,
}; 

const newO = {
  ...o,
  bool: false
} // the same as O but bool: false

To more advanced FP implementation… like compose (super popular function that let you compose function from left to right)

const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)));

EDIT: one important distinction is to know which one is a spread, and which one is a rest parameter.
I used loosely both in my examples for sake of simplicity.

More on the relative docs.


#4

Thanks for the clear and detailed explanations. I think I understand this concept better than the apply method now.