Making plain JS objects iterable — I
I have been brushing up my ES6 skills and trying to wrap my brain around iterators and generators. I wanted to make plain JS objects iterable, using generators. I’ll write about this in two parts. The first part is a bite sized refresher on a few ES6 concepts we will use in the process — iterables, generator functions, symbols etc.
Here’s a quick code snippet to note how we would print keys, values and entries for:
- Iterables (Arrays, Maps, Sets etc.) &
- Plain JS objects which are non-iterable.
For iterable objects in JS
Corresponding functions for plain JS objects
Generator Functions
To quote the book Exploring ES6:
You can think of generators as processes (pieces of code) that you can pause and resume.
Every time a yield
statement is intercepted, the code will pause, returning what you have up until that point, to the caller. Invoking a next()
on the generator object, will allow the generator function to resume, until the next yield
statement, which will continue this pause/play behavior described above. Finally, when there is a return
statement or end of block, the process will end. We can see this in the code below:
/** Output **Returning bulgogi first.
genObj.next() {value: 1, done: false}
Returning samgyeopsal second.
genObj.next() {value: 2, done: false}
genObj.next() {value: "The End", done: true}***/
Symbols
This is the new primitive data type introduced by ES6. We use the Symbol()
factory function to create symbols. The Symbol function is invoked/called, not instantiated.
let mySym1 = new Symbol("foo"); // TypeError
let mySym2 = Symbol("bar");
Every single call to the Symbol function generates a unique new symbol. Note that the function allows you to provide a descriptive string. That’s not the symbol’s value, its just a descriptive text for the symbol you’re creating.
Therefore:
Symbol("foo") === Symbol("foo"); // false
The primary use case for Symbols is : to create unique property keys.
//From the book
const MY_KEY = Symbol();
const obj = {};
obj[MY_KEY] = 123;
console.log(obj[MY_KEY]); // 123
Symbol.iterator
It is the property that specifies the default iterator of an object. This property is NOT writable, enumerable or configurable.
When you read about iteration protocols in JS on MDN, you come across the following:
In order to be iterable, an object must implement the @@iterator method, meaning that the object (or one of the objects up its prototype chain) must have a property with a @@iterator key which is available via constant
Symbol.iterator
Iterable JS objects already have this set up for them, whereas for plain JS objects, we would have to create this key to make them iterable.
The following code demonstrates how to work with Symbol.iterator
for Sets:
let set = new Set([1,2,3,4,5]);
console.log(set); // Set(5) {1, 2, 3, 4, 5}
let iter = set[Symbol.iterator]();
console.log(iter); // SetIterator {1, 2, 3, 4, 5}
iter.next(); // {value: 1, done: false}
In the example above, we use the Symbol.iterator
key of the Set object to get access to its iterator method and then invoke it to get the iterator object. We then call the next()
function of the iterator object, to iterate over the values of set
.
Computed property names & Concise method syntax in Es6
Unlike how they sound, these are two really easy and convenient new features that are provided by ES6. They are better demonstrated by example.
- We assigned the property name to a variable called
prop
. The expression[prop]
evaluates to the value of the variable, i.e.'name'
. This works, we were able to print the name by referring to the property in theconsole.log
statement. - Note how a function expression was not needed to define it, we are able to directly declare the method. A succinct shorthand.
In part II, we will deep dive into making plain JS objects iterable, using generators.
References:
Every web developers source of truth : MDN
The book ‘Exploring ES6’ by Dr. Axel Rauschmayer
(It was recommended to me by my friend and mentor, BP Mishra dada, who guides me in every endeavor to learn anything Web/Frontend.)