# Symbol, Iterator and Generator in JavaScript

Source: https://tpiros.dev/blog/symbol-iterator-and-generator-in-javascript

We're going to look at Symbols, Iterators and Generators in JavaScript (ES2015) and build a practical example that ties all three together.

# Symbol

`Symbol` is a primitive type in JavaScript that guarantees a unique value. We don't know (or care) what the value is. We just know it's unique.

> The ECMAScript standard defines 7 data types, out of which 6 are primitive types in JavaScript: `Boolean`, `Null`, `Undefined`, `Number`, `String`, and of course Symbol. The seventh data type is `Object`.

Symbols are used with object properties. JavaScript gives us two flavours: built-in Symbols (for iteration, for instance) and custom Symbols where we specify our own iteration logic via `Symbol.iterator`.

## Creating a Symbol

Creating one takes a single line:

```javascript
const s = Symbol();
console.log(s); // Symbol()
```

Remember, Symbols are unique:

```javascript
const s1 = Symbol();
const s2 = Symbol();
console.log(s1 === s2); // false
```

Symbols shine when used with objects. Sometimes you want a property that's "secret," one that shouldn't be changed or discovered easily. The old convention was to prefix it with `__` (double underscore). Just a convention, nothing enforced:

```javascript
const obj = {
  name: 'Joe',
  age: 22,
  __: 'secret property value',
};
```

With Symbols, we can hide properties more cleanly:

```javascript
const s = Symbol();
const obj = {
  name: 'Joe',
  age: 22,
};
obj[s] = 'secret property value';
```

Now calling `Object.getOwnPropertyNames(obj)` returns `['name', 'age']` only. The Symbol-keyed property stays hidden.

> Another method `Object.getOwnPropertySymbols(obj)` does return the symbol itself: `[Symbol()]`.

## Try Symbol

# Iterator

An iterator lets us step through a collection. That collection could be an array or anything else. JavaScript ships with a built-in Iterator protocol, but it also lets us create custom iterators.

> For a custom iterator to be valid, it must implement the iterable protocol (via `Symbol.iterator`).

## A basic example

The simplest iterator prints out array values:

```javascript
const numbers = [1, 2, 3];
for (const number of numbers) {
  console.log(number);
}
```

The `for...of` construct uses the Iterator protocol under the hood.

Let's push it a step further:

```javascript
const numbers = [1, 2, 3];
const it = numbers[Symbol.iterator]();
console.log(it.next());
```

This returns `{ value: 1 done: false }`. Two properties: `value` holds the current element, and `done` tells us whether the iteration has finished.

Calling `it.next()` again returns the same structure with `value` set to `2`. Keep calling until you get an object where `done` is `true` and `value` is `undefined`. That means the collection is exhausted.

The `for...of` construct does exactly this behind the scenes, calling `.next()` automatically.

## A custom iterator

Let's build one. Start with an object:

```javascript
const obj = {
  numbers,
};
```

Right now `obj` holds the `numbers` array from earlier. Let's extend it:

```javascript
const obj = {
  numbers,
  idx: 0,
  [Symbol.iterator]() {
    const it = {
      next: () => {
        if (this.idx < this.numbers.length) {
          const number = this.numbers[idx];
          this.idx++;
          return { value: number, done: false };
        } else {
          return { done: true };
        }
      },
    };
  },
};
```

Using the concise method syntax and computed properties from ES2015, we add a `Symbol.iterator` method. It returns an iterator object with a `next` method (required by the protocol). We track our position in the array using the `idx` property.

The logic is simple: grab the current number, increment `idx`, and return `{ value: ..., done: false }`. Once we've run out of numbers, return `{ done: true }`.

Test it with `for...of`:

```javascript
for (const number of obj) {
  console.log(number);
}
```

Since we control the iterator, we can do whatever we want inside it. How about filtering to only prime numbers?

Add this function to the object:

```javascript
isPrime(number) {
  for (let i = 2; i < number; i++) {
    if (number % i === 0) {
      return false;
    }
  }
  return number !== 1 && number !== 0;
}
```

> A prime number is a whole number that is greater than 1 whose only factors are 1 and itself - in other words, it cannot be "created" by multiplying other whole numbers.

Now extend the `next()` implementation:

```javascript
next: () => {
  if (this.idx < this.numbers.length) {
    const number = this.numbers[this.idx];
    if (!this.isPrime(number)) {
      this.idx++;
      return this[Symbol.iterator]().next();
    } else {
      const number = this.numbers[this.idx];
      this.idx++;
      return { value: number, done: false };
    }
  } else {
    return { done: true };
  }
};
```

The line `return this[Symbol.iterator]().next();` skips non-prime numbers. Primes get returned normally with their value.

Run this and only primes come out:

```javascript
// update the numbers array to have more values:
const numbers = [...Array(50 + 1).keys()];

for (const prime of obj) {
  console.log(prime);
}
```

We can also use the spread syntax, because it relies on the Iterator protocol too:

```javascript
const primes = [...obj];
console.log(primes);
```

## Try the iterator

# Generator

A generator is a special function marked with `*`. It returns a generator object and can pause and resume execution. The `yield` keyword handles the pausing.

How does this connect? A generator function's body only runs when stepped through by an iterator. The iterator's `next()` call triggers each `yield`.

Here's a generator that keeps producing IDs:

```javascript
function* generateID() {
  let number = 0;
  while (number < number + 1) {
    yield number++;
  }
}

const it = generateID();
it.next(); // { value: 0, done: false }
it.next(); // { value: 1, done: false }
it.next(); // { value: 2, done: false }
```

> Adding a for...of construct would now create a loop that would keep on generating ID numbers forever - be cautious and don't run into this mistake.

Think back to our custom iterator. If we could build it manually, why not swap in a generator to make the code shorter? We absolutely can:

```javascript
const numbers = [...Array(50 + 1).keys()];

const obj = {
  numbers,
  isPrime(number) {
    for (let i = 2; i < number; i++) {
      if (number % i === 0) {
        return false;
      }
    }
    return number !== 1 && number !== 0;
  },
  *[Symbol.iterator]() {
    for (let i = 0; i < this.numbers.length; i++) {
      const number = this.numbers[i];
      if (this.isPrime(number)) {
        yield number;
      }
    }
  },
};

const primes = [...obj];
console.log(primes);
```

Notice `[Symbol.iterator]()` is now a generator function. Because it's a generator, we can `yield` only the prime numbers and skip everything else.

## Try generator

# Conclusion

Three ES2015 features: Symbols, Iterators and Generators. On their own, each one is useful but fairly narrow. Combine them and they become genuinely powerful, opening up patterns that make JavaScript a more expressive language.
