JS Understanding Iteration protocols

Oleh Baranovskyi
4 min readMay 30, 2018

EcmaScript 6 provides Iteration Protocols which consists of Iterator and Iterable. From my perspective it can be very useful in some cases so in this article we are going to cover this topic.

Our Journey:

  1. Where can be used Iteration Protocols
  2. Understanding Iterator Protocol
  3. Iterable Protocol
  4. Defining loop for non enumerable properties

Where are already used Iteration Protocols

I want to start our journey from the topic “Where are already used Iteration Protocols” because in that case you will understand why you need it.

Array is already built-in iterable. (not only Array but as well String, Map and Set but we will use Array for our examples).

Secondly you need to know “Where to use it” and the answer for that question is really simple:
Objects that implements Iterable protocol can be used in for..of loop and with spread operator.

for…of loop example:

let numbers = [1, 2, 3, 4];for(let number of numbers) {
console.log(number);
}

spread operator example:

let numbers = [1, 2, 3, 4];console.log(...numbers);

You can find more about for…of statement and spread operator on MDN:

  1. for..of loop
  2. Spread operators

Knowledge that object witch implements Iteration protocols can be used in for..of and with spread operator will be enough for now.

Understanding Iterator Protocol

Firstly let’s try to understand what means protocol in our case.
When object implements some protocol it means that there is a convention that says:

“In order to use this protocol your object should implement the following functionality ... with the next rules …”.

Now we are ready to implement Iterator Protocol.

Iterator Protocol - includes the following conventions(rules):

1. Object should implement .next() method.

2. .next() method must return object witch will contain the following properties :

`value` - will be the single unit from iteration

`done` - Has the value true if the iterator is past the end of the iterated sequence. In object that contains done set as true value will be undefined.

Example:

let objWithIteratorProtocol = {
users: [
{id: 1, username: 'Oleh'},
{id: 2, username: 'Erik'},
{id: 3, username: 'John'}
],
nextIndex: 0,
next: function() {
return this.nextIndex < this.users.length
? {value: this.users[this.nextIndex++], done: false}
: {done: true}
}
};
console.log(objWithIteratorProtocol.next()); // { value: { id: 1, username: 'Oleh' }, done: false }
console.log(objWithIteratorProtocol.next()); // { value: { id: 2, username: 'Erik' }, done: false }
console.log(objWithIteratorProtocol.next()); // { value: { id: 3, username: 'John' }, done: false }
console.log(objWithIteratorProtocol.next().done); // true

Iterable Protocol

Iterable Protocol - includes the following conventions:

1. Object that implements Iterable protocol should have Symbol.iterator property.

2. Symbol.iterator should return iterator.

Example:

let objWithItarableProtocol = {
users: [
{id: 1, username: 'Oleh'},
{id: 2, username: 'Erik'},
{id: 3, username: 'John'}
],
nextIndex: 0,
[Symbol.iterator]: function() {
return {
array: this.users,
nextIndex: this.nextIndex,
next: function() {
return this.nextIndex < this.array.length
? {value: this.array[this.nextIndex++], done: false}
: {done: true};
}
};
}
};

Usage:

  1. We can use object iterator (as was in example for iterator protocol), but we should handle each iteration by own:
let iterator = objWithItarableProtocol[Symbol.iterator]();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next().done);
/*Result:
=======
{ value: { id: 1, username: 'Oleh' }, done: false }
{ value: { id: 2, username: 'Erik' }, done: false }
{ value: { id: 3, username: 'John' }, done: false }
true
*/

2. We can use it in for…of loop (for…of loop returns in each iteration value and will be finished when .next() method return object with done property setted to true):

for(let user of objWithItarableProtocol) {
console.log(user);
}
/*
Result:
=======
{ id: 1, username: 'Oleh' }
{ id: 2, username: 'Erik' }
{ id: 3, username: 'John' }
*/

3. Most easy way in my opinion is to use spread operator:

console.log(...objWithItarableProtocol);/*Result:
=======
{ id: 1, username: 'Oleh' } { id: 2, username: 'Erik' } { id: 3, username: 'John' }
*/

But if to be honest I was little bit worry about how it will work in this case:

let otherObject = {
val: 'Some Value'
}
console.log({...otherObject, ...objWithItarableProtocol});

But all seems to be good handled by JavaScript, so here is result:

{ val: 'Some Value',
users:
[ { id: 1, username: 'Oleh' },
{ id: 2, username: 'Erik' },
{ id: 3, username: 'John' } ],
nextIndex: 0,
[Symbol(Symbol.iterator)]: [Function: [Symbol.iterator]] }

In summary: In order to use JavaScript Language features related with iteration we should implement Iteration Protocols (Conventions).

Defining loop for non enumerable properties

Ok, here is one more example that can be helpful in giving you some interesting ideas.

Some times we use third party library and objects related to it. In order to check what object has inside we can use for…in loop construction but it won’t show what is under the hood (non enumerable properties). So we can organize that with Iteration protocols.

The idea is to use for enumerable properties for…in loop and for non enumerable for…of loop.

We have object with two properties one is enumerable and second is not:

let objWithNotEnumerableProps = {
name: 'Object Name'
};
Object.defineProperty(objWithNotEnumerableProps, 'nonEnumerable', {
enumerable: false,
configurable: false,
writable: false,
value: 'Non Enumerable Value'
});
for(let prop in objWithNotEnumerableProps) {
console.log(prop); // "name"
console.log(objWithNotEnumerableProps[prop]); // "Object Name"
}

Since for…in loop will be working as it is, we only should organize logic for for…of loop:

objWithNotEnumerableProps[Symbol.iterator] = function*() {
let props = Object.getOwnPropertyNames(this);
for(let index in props) {
if(!this.propertyIsEnumerable(props[index])) yield props[index];
}
}
for(let prop of objWithNotEnumerableProps) {
console.log(prop); // "nonEnumerable"
console.log(objWithNotEnumerableProps[prop]); // "Non Enumerable Value"
}

…and one more, we can use Object.keys method for enumerable properties and spread operator for non enumerable:

console.log(Object.keys(objWithNotEnumerableProps)); // ["name"]console.log([...objWithNotEnumerableProps]); // ["nonEnumerable"]

Conclusion

Thank you guys for reading. I hope you enjoyed it and learned some new stuff related to JavaScript. Please subscribe and press ‘Clap’ button if you like this article.

--

--

Oleh Baranovskyi
Oleh Baranovskyi

Written by Oleh Baranovskyi

Frontend Lead & Architect | Web community manager https://obaranovskyi.com/

No responses yet