I found some code online. I've squashed the original code down into this little excerpt, that when ran, will print 1-20 to the console.
var NumbersFromOne = { *[Symbol.iterator] () { for (let i = 1;; ++i) yield i; } }; var take = function* (numberToTake, iterable) { let remaining = numberToTake; for (let value of NumbersFromOne) { if (remaining-- <= 0) break; yield value; } } var printToTwenty = take(20, NumbersFromOne) console.log(...printToTwenty);
Now, I understand that take() is a GeneratorFunction.
When take() is called, it is given an iterator.
The code "...printToTwenty" uses the spread operator to iterate through that function.
I understand that NumbersFromOne is an object. I've come here looking for an explanation of what this part means:
*[Symbol.iterator] () {}
Declaring generator functions is done like this: function* () {}
So I'm assuming this isn't declaring a generator function.
* also doesn't represent the function name
* also can't be replaced with another operator (/, -, +)
What is the deal with that syntax, and why is the * before [Symbol.iterator]
If placed after, it will not run.
I had considered that *[Symbol.iterator] () is a way to overwrite the existing iterator property, but then wouldn't it say this[Symbol.iterator].
Thanks!
Symbol. iterator is the protocol that makes native objects like Array , Set , and Map iterable by providing a hook into language features like for…of loops and the spread operator. The most obvious use case is in creating new, iterable data structures that are not provided by the language, like a Linked List.
A JavaScript iterable is an object that has a Symbol. iterator. The Symbol. iterator is a function that returns a next() function. An iterable can be iterated over with the code: for (const x of iterable) { }
The iterable protocol 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 : [Symbol.
The primary purpose of an iterator is to allow a user to process every element of a container while isolating the user from the internal structure of the container. This allows the container to store elements in any manner it wishes while allowing the user to treat it as if it were a simple sequence or list.
There are a few things that might make this code look complicated:
It uses the object property shorthand notation. What you're seeing here is actually the following:
var NumbersFromOne = { [Symbol.iterator]: function* () { for (let i = 1;; ++i) yield i; } };
Symbol.iterator
creates a custom iterator for your NumbersFromOne
object.
So your code basically means that the iterator of NumbersFromOne
is defined as a generator. Instead of manually having to define a function which returns a next and other properties:
var NumbersFromOne = { [Symbol.iterator]: function () { var i = 1; return { next: function() { return { value: i++, done: false }; } }; } };
Returning the generator creates the next
function automatically for. This allows you to yield when you need to.
It can then be called as:
const it = NumbersFromOne[Symbol.iterator](); it.next(); // 1 it.next(); // 2 it.next(); // 3 // ...
Note: Written this way, this iterator never ends! So if you were to call it in a for ... of
loop without an end-condition, it would freeze your program.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With