ES6's new array methods, collections, for-of loops, expansion operators (...) and even asynchronous programming rely on iterator implementations. This article will explain the iterators and generators of ES6 in detail, and further explore the internal principles and usage methods of iterable objects.
one, the principle of iterator
When processing arrays or collections in programming languages, using loop statements must initialize a variable to record iteration positions, and programmatically using iterators can simplify this data operation
How to design an iterator?
The iterator itself is an object. This object has the next( ) method to return the result object. This result object has the next return value value, iterative completion boolean value done, simulation creates a simple iterator as follows:
function createIterator(iterms) { let i = 0 return { next() { let done = (i >= ) let value = !done ? iterms[i++] : undefined return { done, value } } } } let arrayIterator = createIterator([1, 2, 3]) (()) // { done: false, value: 1 } (()) // { done: false, value: 2 } (()) // { done: false, value: 3 } (()) // { done: true, value: undefined }
If you are confused about the above syntax, please refer to:【ES6】New functions and deconstruction assignment of objects
Each time the iterator is called next() returns the next object until the dataset is exhausted.
The writing rules of iterators in ES6 are similar, but the generator object is introduced to create iterator objects more simply.
2. Create an iterator
ES6 encapsulates a generator to create iterators. Obviously the generator is a function that returns the iterator. This function is represented by the asterisk (*) after function, and uses the new internal special keyword yield to specify the return value of the iterator's next( ) method.
How to create an iterator using ES6 generator?A simple example is as follows:
function *createIterator() { yield 123; yield 'someValue' } let someIterator = createIterator() (()) // { value: 123, done: false } (()) // { value: 'someValue', done: false } (()) // { value: undefined, done: true }
Use the yield keyword to return any value or expression, and you can add elements in batches to the iterator:
// let createIterator = function *(items) { // Generator function expressionfunction *createIterator(items) { for (let i = 0; i < ; i++) { yield items[i] } } let someIterator = createIterator([123, 'someValue']) (()) // { value: 123, done: false } (()) // { value: 'someValue', done: false } (()) // { value: undefined, done: true }
Since the generator itself is a function, it can be added to the object, and used as follows:
let obj = { // createIterator: function *(items) { // ES5 *createIterator(items) { // ES6 for (let i = 0; i < ; i++) { yield items[i] } } } let someIterator = ([123, 'someValue'])
A feature of generator functions is thatAfter executing a yield statement, the function will automatically stop executing, call the next( ) method of the iterator again before continuing to execute the next yield statement.
This ability to automatically abort function execution derives many advanced uses.
III. Iterable objects
Commonly used in ES6Collection objects (arrays, Set/Map collections) and strings are iterable objects, these objects have default iterators and properties.
Iterators created through generators are also iterable objects, because the generator will assign values to attributes by default.
3.1
Iterable objects haveattribute, i.e.
The objects of the attribute have default iterators.
We can useTo access the default iterator of an object, for example, for an array:
let list = [11, 22, 33] let iterator = list[]() (()) // { value: 11, done: false }
Obtain the default iterator for the iterable object of the array and operate iterate over the elements in the array.
Otherwise, we can useTo detect whether the object is an iterable object:
function isIterator(obj) { return typeof obj[] === 'function' } (isIterator([11, 22, 33])) // true (isIterator('sometring')) // true (isIterator(new Map())) // true (isIterator(new Set())) // true (isIterator(new WeakMap())) // false (isIterator(new WeakSet())) // false
Obviously, arrays, Set/Map collections, and strings are all iterable objects, while WeakSet/WeakMap collections (weak reference collections) are not iterable.
3.2 Create an iterable object
By default, custom objects are not iterable.
As I just said, an iterator created through a generator is also an iterable object, and the generator will assign values to attributes by default.
So how do you turn a custom object into an iterable object? By adding a generator to the property:
let collection = { items: [11,22,33], *[]() { for (let item of ){ yield item } } } (isIterator(collection)) // true for (let item of collection){ (item) // 11 22 33 }
Array items are iterable objects, and collection objects also become iterable objects by assigning values to attributes.
3.3 for-of
I noticed that the last chestnut used for-of instead of index loop, which is a new feature added by ES6 for iterable objects.
Think about the implementation principle of for-of loop.
For iterable objects using for-of, for-of calls next( ) of the iterable object every time it is executed, and stores the return result in a variable, and continues to execute until the iterable object's done property value is false.
// Iterate over a stringlet str = 'somestring' for (let item of str){ (item) // s o m e s t r i n g }
Essentially, for-of calls the property method of the str string to get the iterator (this process is done by the JS engine), and then calls the next( ) method multiple times to store the object value value in the item variable.
Willfor-of
An error will be reported when used for non-iterable objects, null or undefined!
3.4 Expand operator (...)
The ES6 syntax sugar expansion operator (...) also serves iterable objects, that is, you can only "expand" arrays, collections, strings, and custom iterable objects.
The following chestnut outputs the results of calculations of different iterable object expansion operators:
let str = 'somestring' (...str) // s o m e s t r i n g let set = new Set([1, 2, 2, 5, 8, 8, 8, 9]) (set) // Set { 1, 2, 5, 8, 9 } (...set) // 1 2 5 8 9 let map = new Map([['name', 'jenny'], ['id', 123]]) (map) // Map { 'name' => 'jenny', 'id' => 123 } (...map) // [ 'name', 'jenny' ] [ 'id', 123 ] let num1 = [1, 2, 3], num2 = [7, 8, 9] ([...num1, ...num2]) // [ 1, 2, 3, 7, 8, 9 ] let udf (...udf) // TypeError: undefined is not iterable
As can be seen from the above code, the expansion operator (...) can easily convert iterable objects into an array. Like for-of, the expansion operator (...) will report an error when used for non-iterable objects, null or undefined!
4. Default iterator
ES6 provides default iterators for many built-in objects. Only when the built-in iterators cannot meet the needs are created.
The three set objects of ES6: Set, Map, and Array all have default iterators. Commonly used methods such as values() and entries() methods all return an iterator, and the values are as follows:
- entries(): multiple key-value pairs
- values(): the value of the set
- keys(): keys of the set
Calling the above methods can get the iterator of the collection and use a for-of loop. The example is as follows:
/******** Map ***********/ let map = new Map([['name', 'jenny'], ['id', 123]]) for(let item of ()){ (item) // [ 'name', 'jenny' ] [ 'id', 123 ] } for(let item of ()){ (item) // name id } for (let item of ()) { (item) // jenny 123 } /******** Set ***********/ let set = new Set([1, 4, 4, 5, 5, 5, 6, 6,]) for(let item of ()){ (item) // [ 1, 1 ] [ 4, 4 ] [ 5, 5 ] [ 6, 6 ] } /********* Array **********/ let array = [11, 22, 33] for(let item of ()){ (item) // [ 0, 11 ] [ 1, 22 ] [ 2, 33 ] }
In addition, both String and NodeList types have default iterators. Although no other methods are provided, you can use a for-of loop.
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.