Advanced js: iterable objects (@@iterator)
Tags: javascript, tutorial, node, webdev
Published: 2022-12-21 15:53:40


Iterate over data structures is like `Programming 101`. 

This just works:

```js
for (const item of [1,2,3]) { 
  console.log(item)
}
// 1
// 2
// 3
```

or using a [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)

```js
const map = new Map()
m.set('a', 'first value')
m.set('b', 'second value')

for (const [key, value] of m) {
  console.log(key + " » " + value)
} 
// a » first value
// b » second value
```

But, not saying anything new, [Objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) are not iterable

```js
const obj = { a: 'item a', b: 'item b' } 

for (const [key, value] of obj) {
  console.log(key + " » " + value)
}
// ⚠️ Uncaught TypeError: obj is not iterable
```

### why? 🤔

`Array`, `Map`, `Set`... built-in objects implement the [**iterable protocol**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) while `Object` doesn't. 

Summarizing official docs, any object (or one of the objects up its prototype chain) which defines the  **`@@iterator` method** is iterable. 

**`@@iterator` method** is just notation. A convention for naming "a method that is accessible through `Symbol.iterator` symbol that [[_fulfills the Iteration protocol_]]"

### ... wait what? 🙃

This notation: `Foo[@@someKey]` is naming the property of `Foo` accessible through `Symbol.someKey`; easier to understand with code:

```js
{
  ...
  [Symbol.someKey]: /* we're naming this property */
}
```

> Really I haven't found any official mention in the docs explaining this notation but inferred from usage across the docs.

When the docs refers to **`@@iterator`**:

```js
{
  ...
  [Symbol.iterator]: () => {
    /* we need to implement this method */
  } 
}
```

Now, js needs this method to... _return an object with a method named next() that returns bla bla bla_; something like this:

```js
{
  ...
  [Symbol.iterator]: () => {
    return {
      next() {
        ...
        return { done: true, value: i };
      },
    }
  }
}
```

**OR** (and this is really cool) this function can be a [Generator function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator) 

from the docs: 

> The Generator object is returned by a generator function and it conforms to both the iterable protocol and the iterator protocol.

```js
{
  ...
  *[Symbol.iterator]() {
    /* a generator function here does the trick */
    yield i
  }
}
```

_To Be Honest_: I have never found the chance to use generator functions in real life... they are just one more tool in my toolbox 🧰

### Assemble! 🦸🏾‍♂️

An object that defines (in `@@iterator`) a function that yields each `[key, value]`, **is iterable**:

```js
const obj = {
  a: 'first value',
  b: 'second value',

  *[Symbol.iterator] () {
    for (const k in this) {
      yield [k, this[k]]
    }
  },
}

for (const [key, value] of obj) {
  console.log(key + " : " + value)
}
// a : first value
// b : second value
```

Since it's iterable, it may also use destructuring: 

```js
const obj = {
  a: 3,
  b: 100,

  *[Symbol.iterator] () {
    for (const k in this) {
      yield [k, this[k]]
    }
  },
}

[...obj].find(([_, value]) => value > 99 )
// Array [ "b", 100 ]
```

***

I've uploaded to [`npm` a tiny utility package](https://www.npmjs.com/package/@raw-js/iterable) that does this for you in case you are curious:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gmalmxz4l8jzq7cgj1xd.png)

```js
import { iterable } from "@raw-js/iterable";

const obj = { ... }

/* for..of */
for (const [key, value] of iterable(obj)) {
  console.log(key + ": " + value);
}

/* destructuring */
[...iterable(obj)]
```

***

### Final thoughts 🧳

Do I include this method on the prototype chain of my objects, in order to make them iterable?

NO.

I find this an interesting exercise for deep-understanding js. But that's it. This is a "_how does this work_" exercise.

In real life, js does implement this idea thanks to [`Object.entries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries)

```js
for (const [key, value] of iterable(obj)) {}

===>

for (const [key, value] of Object.entries(obj)) {}
```

Implementing [`@raw-js/iterable`](https://www.npmjs.com/package/@raw-js/iterable) helped me understanding more about Symbols, Iterators, Generators, etc. But in my day to day work I'd prefer `Object.entries()`

***

Banner image from [Storyset](https://storyset.com)

Thanks for reading 💚.