ES6 adds a few new features that allows for more expressive and concise way of declaring parameters for various use cases. Let’s have a look at the new default parameter and rest parameters and understand their use.

Default Parameter

ES6 has introduced a new way of defining default argument to parameters. This can be applied to any type of function, whether it’s an arrow function, conventional function or class constructor function.

Previously, in ES5, defining a default parameter was done in the body of the function itself. For Example:

function sayHello(name) {
    if(name === undefined)
        name = 'Dave'
    console.log('Hello ' + name)
}

sayHello() // Hello Dave
sayHello('Marry') // Hello Marry

Now, with default parameters available to us, we can do the same thing without having to do the check for undefined and assignment ourselves. We can do the same thing with the following snippet:

function sayHello(name = 'Dave') {
    console.log('Hello ' + name)
}

sayHello() // Hello Dave
sayHello('Marry') // Hello Marry

Here’s the same function using more ES6, namely arrow function, and string templating that we will cover in another part of these series:

const sayHello = (name = 'Dave') => {
    console.log(`Hello ${name}`)
}

sayHello() // Hello Dave
sayHello('Marry') // Hello Marry

Note that JavaScript does not support overloading like some other languages such as C# and Java. So this becomes really useful as it makes it easier to define optional parameters with defaults.

Rest Parameters

Rest Parameters allows handling of multiple arguments as an array without having the need to touch the arguments object.

Rest Parameters can be only be used for the last parameter of any function, any argument passed to the rest parameter is automatically becomes an Array.

Let’s see how we would do this same type of functionality in ES5:

function sayHelloToEveryone() {
    var names = Array.prototype.slice.call(arguments) // we convert arguments to an array
    names.forEach(function(name) {
        console.log('Hello ' + name)
    });
}

sayHelloToEveryone('Dave', 'John', 'Martin', 'Sandra')

There are two noticeable problems here, firstly, arguments seems to be magic, it’s not been declared, yet it’s there. Secondly, In ES5, the arguments object is not an Array, so we can’t utilise Array’s prototype functions such as forEach, map etc. without having to map arguments data into an Array object first.

Let’s have a look at a simple example of how this would be done in ES6’s rest parameter:

const sayHelloToEveryone = (...names) => {
    names.forEach((name) => {
        console.log(`Hello ${name}`)
    })
}

sayHelloToEveryone('Dave', 'John', 'Martin', 'Sandra')

What if no argument gets passed?

const f = (...a) => {
  console.log(a)
}

f() // prints empty array []

We get an empty array, so we don’t need to handle undefined.

A Rest Parameter must be the last parameter

This is allowed:

const f = (a, ...b) => {
  // ...
}

But NOT this:

cons f = (...a, b) => {
  // ...
}

Spread Operator

Similarly to the rest parameter in terms of syntax, but not in usage is the spread operator. The spread operator allows for a shorter way of concatenation Iterable objects when creating a new array or applying an array of arguments to a function.

let arrayA = [1,2,3]
let arrayB = [4,5,6]
let arrayC = [...arrayA, ...arrayB]
console.log(arrayC) // [1,2,3,4,5,6]

Here’s an example of how an array of arguments is applied to a function:

let sum = (...nums) => { // --> rest
  let result = 0
  nums.forEach((num) => {
    result += num
  })
  return result
}

let nums = [1,2,3,4,5]
let result = sum(...nums) // --> spread
console.log(result) // 15

The ES5 equivalent is:

var sum = function sum() {
  nums = Array.prototype.slice.call(arguments)
  var result = 0
  nums.forEach(function (num) {
    result += num
  });
  return result;
}

var nums = [1, 2, 3, 4, 5]
var result = sum.apply(undefined, nums)
console.log(result)

Common Use Case

The most common use case that you’ll find for spread operator is shallow merging.

var foo = {
  a: 1,
  b: 2
}

var bar = {
  b: 3
}

var fooBar = {
  ...foo,
  ...bar
}

The value of fooBar will be

{
  a: 1,
  b: 3
}

What’s the Distinction between Rest Parameter and Spread Operator?

The main thing to remember when trying to distinguish Rest Parameter and Spread Operator is that the former (Rest Parameter) is done only as a parameter of a function (destructuring is counted as a function) i.e.

let f = (...rest) => {
    // ...
}

Where as the latter (Spread Operator) is done as part of anything else e.g. part of an argument, array concatenating etc.