Home  >  Article  >  Web Front-end  >  Introduction to the principles of iterators and iterable objects in ES6 (with examples)

Introduction to the principles of iterators and iterable objects in ES6 (with examples)

不言
不言forward
2018-10-29 15:34:362626browse

This article brings you an introduction to the principles of iterators and iterable objects in ES6 (with examples). It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you. help.

ES6 new array methods, collections, for-of loops, spread operators (...) and even asynchronous programming all rely on iterator (Iterator) implementation. This article will explain in detail the iterators and generators of ES6, and further explore the internal principles and usage methods of iterable objects

1. The principles of iterators

Processing arrays in programming languages When using a loop statement or a collection, you must initialize a variable to record the iteration position, and using iterators programmatically 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 and the iteration completion Boolean value done, which simulates the creation of a simple iterator. As follows:

function createIterator(iterms) {
  let i = 0
  return {
    next() {
      let done = (i >= iterms.length)
      let value = !done ? iterms[i++] : undefined
      return {
        done,
        value
      }
    }
  }
}

let arrayIterator = createIterator([1, 2, 3])

console.log(arrayIterator.next()) // { done: false, value: 1 }
console.log(arrayIterator.next()) // { done: false, value: 2 }
console.log(arrayIterator.next()) // { done: false, value: 3 }
console.log(arrayIterator.next()) // { done: true, value: undefined }
If you are confused about the above syntax, you can refer to: Detailed explanation of the new functions and destructuring assignment of objects in ES6 (code examples)

Every time next() of the iterator is called, Return the next object until the data set is exhausted.

The rules for writing iterators in ES6 are similar, but generator objects are introduced to make it easier to create iterator objects

2. Create iterators

ES6 encapsulates a generator to create iterators. Obviously a generator is a function that returns an iterator, which is represented by an 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()

console.log(someIterator.next()) // { value: 123, done: false }
console.log(someIterator.next()) // { value: 'someValue', done: false }
console.log(someIterator.next()) // { value: undefined, done: true }

Use the yield keyword to return any value or expression, and you can add elements to the iterator in batches:

// let createIterator = function *(items) { // 生成器函数表达式
function *createIterator(items) {
  for (let i = 0; i < items.length; i++) {
    yield items[i]
  }
}

let someIterator = createIterator([123, &#39;someValue&#39;])

console.log(someIterator.next()) // { value: 123, done: false }
console.log(someIterator.next()) // { value: &#39;someValue&#39;, done: false }
console.log(someIterator.next()) // { value: undefined, done: true }

Since the generator itself is a function, it can be added into the object, and is used as follows:

One feature of the
let obj = {
  // createIterator: function *(items) { // ES5
  *createIterator(items) { // ES6
    for (let i = 0; i < items.length; i++) {
      yield items[i]
    }
  }
}
let someIterator = obj.createIterator([123, &#39;someValue&#39;])
generator function is that the function will automatically stop execution after executing a yield statement, and the next() method of the iterator will be called again to continue executing the next yield statement. .
This ability to automatically terminate function execution has led to many advanced uses.

3. Iterable objects

The commonly used collection objects (arrays, Set/Map collections) and strings in ES6 are iterable objects, and these objects have default Iterators and the Symbol.iterator property.

The iterator created by the generator is also an iterable object, because the generator assigns a value to the Symbol.iterator property by default.

3.1 Symbol.iterator

Iterable objects have the Symbol.iterator property, that is, objects with the Symbol.iterator property have default iterators.

We can use Symbol.iterator to access the default iterator of an object, for example, for an array:

let list = [11, 22, 33]
let iterator = list[Symbol.iterator]()
console.log(iterator.next()) // { value: 11, done: false }

Symbol.iterator obtains the default iterator of the iterable object of the array and operates on it Iterates over the elements in the array.

On the contrary, we can use Symbol.iterator to detect whether the object is an iterable object:

function isIterator(obj) {
  return typeof obj[Symbol.iterator] === &#39;function&#39;
}

console.log(isIterator([11, 22, 33])) // true
console.log(isIterator(&#39;sometring&#39;)) // true
console.log(isIterator(new Map())) // true
console.log(isIterator(new Set())) // true
console.log(isIterator(new WeakMap())) // false
console.log(isIterator(new WeakSet())) // false

Obviously arrays, Set/Map collections, and strings are all iterable objects, and WeakSet/WeakMap Collections (weak reference collections) are not iterable.

3.2 Creating iterable objects

By default, custom objects are not iterable.

As mentioned just now, the iterator created by the generator is also an iterable object, and the generator will assign a value to the Symbol.iterator property by default.

So how to turn a custom object into an iterable object? By adding a generator to the Symbol.iterator property:

let collection = {
  items: [11,22,33],
  *[Symbol.iterator]() {
    for (let item of this.items){
      yield item
    }
  }
}

console.log(isIterator(collection)) // true

for (let item of collection){
  console.log(item) // 11 22 33
}

The array items is an iterable object, and the collection object also becomes an iterable object by assigning a value to the Symbol.iterator property.

3.3 for-of

Notice that the last chestnut uses for-of instead of index loop. for-of is a new feature added by ES6 for iterable objects.

Think about the implementation principle of for-of loop.

For an iterable object using for-of, each time for-of is executed, it will call next() of the iterable object, and store the return result in a variable, and continue to execute until the iterable object The done attribute value is false.

// 迭代一个字符串
let str = &#39;somestring&#39;

for (let item of str){
  console.log(item) // s o m e s t r i n g
}

Essentially, for-of calls the Symbol.iterator property method of the str string to obtain the iterator (this process is completed by the JS engine), and then calls the next() method multiple times to store the object value in item variable.

Using for-of for non-iterable objects, null or undefined will report an error!

3.4 Expansion operator (...)

ES6 syntax sugar expansion operator (...) also serves iterable objects, that is, it can only "expand" arrays, Collections, strings, custom iterable objects.

The following example outputs the results calculated by different iterable object expansion operators:

let str = &#39;somestring&#39;
console.log(...str) // s o m e s t r i n g
let set = new Set([1, 2, 2, 5, 8, 8, 8, 9])
console.log(set) // Set { 1, 2, 5, 8, 9 }
console.log(...set) // 1 2 5 8 9
let map = new Map([[&#39;name&#39;, &#39;jenny&#39;], [&#39;id&#39;, 123]])
console.log(map) // Map { &#39;name&#39; => 'jenny', 'id' => 123 }
console.log(...map) // [ 'name', 'jenny' ] [ 'id', 123 ]
let num1 = [1, 2, 3], num2 = [7, 8, 9]
console.log([...num1, ...num2]) // [ 1, 2, 3, 7, 8, 9 ]
let udf
console.log(...udf) // TypeError: undefined is not iterable

As can be seen from the above code, the expansion operator (...) can easily convert iterable objects is an array. Like for-of, the spread operator (...) will report an error when used on non-iterable objects, null or undefined!

4. Default iterator

ES6 为很多内置对象提供了默认的迭代器,只有当内建的迭代器不能满足需求时才自己创建迭代器。

ES6 的 三个集合对象:Set、Map、Array 都有默认的迭代器,常用的如values()方法、entries()方法都返回一个迭代器,其值区别如下:

entries():多个键值对

values():集合的值

keys():集合的键

调用以上方法都可以得到集合的迭代器,并使用for-of循环,示例如下:

/******** Map ***********/
let map = new Map([['name', 'jenny'], ['id', 123]])

for(let item of map.entries()){
  console.log(item) // [ 'name', 'jenny' ]  [ 'id', 123 ]
}
for(let item of map.keys()){
  console.log(item) // name id
}
for (let item of map.values()) {
  console.log(item) // jenny 123
}

/******** Set ***********/
let set = new Set([1, 4, 4, 5, 5, 5, 6, 6,])

for(let item of set.entries()){
  console.log(item) // [ 1, 1 ] [ 4, 4 ] [ 5, 5 ] [ 6, 6 ]
}

/********* Array **********/
let array = [11, 22, 33]

for(let item of array.entries()){
  console.log(item) // [ 0, 11 ] [ 1, 22 ] [ 2, 33 ]
}

此外 String 和 NodeList 类型都有默认的迭代器,虽然没有提供其它的方法,但可以用for-of循环

The above is the detailed content of Introduction to the principles of iterators and iterable objects in ES6 (with examples). For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:segmentfault.com. If there is any infringement, please contact admin@php.cn delete