Custom Spread Operator [...5]

In javascript, spread operator is denoted by (3 dots), basically it takes an array or object or string and can copy or expand or concat or merge its items to another variable.

And we will not be looking into rest parameters which looks similar to spread operator but used for destructuring array and objects.

var str = 'Hello World';
console.log(...str); // H e l l o   W o r l d

var obj = { name: 'My Car', modelYear: '2018' };
var obj2 = { model: 'Ferrari' };
console.log({ ...obj, obj2 }); // { "name": "My Car", "modelYear": "2018", "model": "Ferrari" }

var arr = [1, 2, 3, 4, 5];
console.log(...arr); // 1 2 3 4 5

First, we will see its pros and cons and how it works then we will implement it. Let us start with the cons.

Cons:

Pros:

Now we have an idea of what spread operator is and what it can and can't do. Let's implement our custom spread operator step by step.

// This is how our Custom Spread Operator will look like
console.log([...5]); // [1, 2, 3, 4, 5]

// Above custom spread operator will not work now and
// We will get the following error
// Uncaught TypeError: number 5 is not iterable (cannot read property Symbol(Symbol.iterator))

Step 1:

What is Symbol.iterator?

  1. Symbol.iterator let us to create a custom iterator function (not possible before ES6).
  2. It uses iterator protocol which defines a standard way to produce a sequence of values which is either finite or in-finite and return those values when all values are done or generated.
  3. Iterable data sources are Array, Sets, Maps, Objects, arguments and DOM{:html} (Eg: NodeList) etc,.

Symbol.iterator contains two parts.

  1. An object whose key is Symbol.iterator called Iterable.
  2. A function for the above key called Iterator to iterate the values passed to it.

Example:

var myCustomIterator = {
  [Symbol.iterator]: function () {},
};

Simple right? 😆

Note: By default functions are not iterable.

In the below example, we will create a simple iterable function to understand more.

// A simple function which will accept a parameter called `n`
// and it return myCustomIterator.

var generateNumbers = function (n) {
  var myCustomIterator = {
    [Symbol.iterator]: function () {
      var i = 1; // Statement 1
      return {
        next: () => {
          return {
            value: i++, // Statement 3
            done: i === n + 2, // Statement 2
          };
        },
      };
    },
  };

  return myCustomIterator;
};

generateNumbers(); // return an iterable function

Explanation for above code:

To make sure our generateNumbers() is iterable we will use for of and spread operator loop in below example:

for (var i of generateNumbers(5)) {
  console.log(i); // prints from 1 to 5
}

// It works 😆

// Now we can do this
[...generateNumbers(5)]; // prints from 1 to 5

// TADA 😁

Cool right? We made a function iterable.

In our next and final step, we will make generateNumbers() as a native feature so that we can do this [...5] instead of [...generateNumbers(5)].

// Add Symbol.iterator to Number prototype
Number.prototype[Symbol.iterator] = function () {
  var i = 1; // Statement 1
  return {
    next: () => {
      return {
        value: i++, // Statement 3
        done: i === this + 2, // Statement 2
      };
    },
  };
};

// Now I can do this 🤪
console.log([...5]); // [1, 2, 3, 4, 5]

Final thoughts

Thanks to TC39 committee and ES6 specs, we were able to write our own custom iterator function with spread operator. Would love to know how you are going to use Symbol.iterator now that you have learned about it.


If you liked it tweet about it. Hoping you learned something new about javascript today. My next post is how a virtual DOM works. And if you are not subscribed, subscribe below :D

References:

Join 300+ Readers

Sent out once every month. No spam 🤞