ES6迭代器详细介绍

目录

1、迭代器是什么

2、迭代器实现遍历的原理

3、如何让对象实现for-of遍历

4、内置迭代器——keys()、values()、entries() 


1、迭代器是什么

迭代器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。

Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。

Iterator接口就是对象里面的一个属性,这个属性叫做Symbol.iterator。而原生具备 Iterator 接口的数据结构有Array、Map、Set、String、TypedArray、arguments、NodeList ,它们都可以使用for-of来遍历。

举个:

我们使用for-of来遍历一下数组:

let arr = [23, 90, 45, 12, 'hello']
for (let i of arr) {
  console.log(i);
}

返回结果如下:

ES6迭代器详细介绍_第1张图片

数组可以使用for-of实现遍历,是因为数组对象里面有一个Symbol.iterator属性。我们可以打印看一下:

ES6迭代器详细介绍_第2张图片

ES6迭代器详细介绍_第3张图片 

当我们使用for-of来遍历对象时:

let obj = {
  name: 'zhangsan',
  age: 19
}
for (let i of obj) {
  console.log(i);
}

 结果报错了:obj不可迭代

ES6迭代器详细介绍_第4张图片

 这是因为对象没有Iterator 接口,即对象原型上没有Symbol.iterator属性,所以无法使用for-of来遍历对象。

2、迭代器实现遍历的原理

(1)创建一个指针对象(由Symbol.iterator来创建),指向当前数据结构的起始位置

(2)第一次调用对象的next方法,指针自动指向数据结构的第一个成员

(3)接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员

(4)每次调用next方法都会返回一个包含value和done属性的对象(value 表示当前的值,done 表示遍历是否结束,true表示结束了,false表示还没有结束)

这实际上也是for-of实现遍历的原理。

let arr = [23, 90, 45, 12, 'hello']
for (let i of arr) {
  console.log(i);
}

上面的数组能够实现遍历的原理如下:

let arr = [23, 90, 45, 12, 'hello']
// for (let i of arr) {
//   console.log(i);
// }

// 1.使用Symbol.iterator创建一个迭代器对象
let iterator = arr[Symbol.iterator]()
// 2.调用迭代器对象的next方法,每次调用next方法,指针都会往后移动,直到指向最后一个成员
//每次调用next方法都会返回一个包含value和done属性的对象
console.log(iterator.next()); //{ value: 23, done: false }
console.log(iterator.next()); //{ value: 90, done: false }
console.log(iterator.next()); //{ value: 45, done: false }
console.log(iterator.next()); //{ value: 12, done: false }
console.log(iterator.next()); //{ value: "hello", done: false }
console.log(iterator.next()); //{ value: undefined, done: true }
console.log(iterator.next()); //{ value: undefined, done: true }

for-of 实现原理就是调用迭代器的next()方法,第一次调用将指针指向数据结构的第一个成员,依次调用依次指向,直到没有成员可以指向,done为true。

3、如何让对象实现for-of遍历

要想让对象也能够使用for-of来遍历,那么我们就需要给对象添加一个Iterator 接口,也就是在对象的原型上手动添加一个Symbol.iterator属性。

简单实现思路如下:

1、给对象添加一个iterator接口,即Symbol.iterator属性

2、Symbol.iterator创建一个指针对象,因此我们返回一个对象

3、指针对象里面有一个next方法

4、每次调用next方法返回一个包含value和done属性的对象

let obj = {
  name: "zhangsan",
  age: 19,
  gender: 'male',
  // 1.给对象添加一个iterator接口,即Symbol.iterator属性
  [Symbol.iterator]() {
      // 2.Symbol.iterator创建一个指针对象,因此我们返回一个对象
      return {
        // 3.指针对象里面有一个next方法
        next: function () {
          // 4.每次调用next方法返回一个包含value和done属性的对象
          return {
            value: '123',
            done: false
           }
         }
       }
   }
}

for (let i of obj) {
  console.log(i);
}

5、控制指针往后移动,并添加结束条件

let obj = {
  name: "zhangsan",
  age: 19,
  gender: 'male',
  // 1.给对象添加一个iterator接口,即Symbol.iterator属性
  [Symbol.iterator]() {
    // 设置索引变量
    let index = 0
    // 2.Symbol.iterator创建一个指针对象,因此我们返回一个对象
    return {
      // 3.指针对象里面有一个next方法
      //这里需要使用箭头函数,否则this指向的就不是obj当前对象了
      next: () => {
        // 4.每次调用next方法返回一个包含value和done属性的对象
        if (index < Object.values(this).length) {
          // Object.values()方法返回对象的属性值构成的数组
          let result = {value: Object.values(this)[index], done: false}
          // 下标自增,让指针往后移动
          index++
          return result
        } else {
          //遍历结束
          return {value: undefined, done: true}
        }
      }
    }
  }
}
for (let i of obj) {
  console.log(i);
}

运行结果如下:
ES6迭代器详细介绍_第5张图片

4、内置迭代器——keys()、values()、entries() 

为了更好地访问对象的内容,ES6为数组、Set、Map集合内置了三个API:keys()、values()、和entries() ,它们都返回一个迭代器对象,因为它们都返回一个迭代器对象,因此我们可以使用它们的返回值去调用next()方法,从而实现遍历。其中:

keys() 用来遍历所有的键名;

values() 用来遍历所有的键值;

entries() 用来遍历[键名, 键值]组成的数组。(对于数组,键名就是索引值)

我们以数组为例,使用一下这些方法来实现遍历:

let arr = ['zhangsan', 'lisi', 'hello']
let keys = arr.keys(); 
let values = arr.values();
let entries = arr.entries();

console.log(keys.next()); //{ value: 0, done: false }
console.log(keys.next()); //{ value: 1, done: false }
console.log(keys.next()); //{ value: 2, done: false }
console.log(keys.next()); //{ value: undefined, done: true }

console.log(values.next()); //{ value: 'zhangsan', done: false }
console.log(values.next()); //{ value: 'lisi', done: false }
console.log(values.next()); //{ value: 'hello', done: false }
console.log(values.next()); //{ value: undefined, done: true }

console.log(entries.next()); //{ value: [ 0, 'zhangsan' ], done: false }
console.log(entries.next()); //{ value: [ 1, 'lisi' ], done: false }
console.log(entries.next()); //{ value: [ 2, 'hello' ], done: false }
console.log(entries.next()); //{ value: undefined, done: true }

for (let k of keys) {
  console.log(k);
}
/*
上面的for-of输出结果:
0
1
2
*/

for (let k of values) {
  console.log(k);
}
/*
上面的for-of输出结果:
zhangsan
lisi
hello
*/

for (let k of entries) {
  console.log(k);
}
/*
上面的for-of输出结果:
[ 0, 'zhangsan' ]
[ 1, 'lisi' ]
[ 2, 'hello' ]
*/

// 手动调用next 遍历迭代器对象 {value:0 ,done:false}
let result;
while (!(result = values.next()).done) {
  console.log(result);
}
/*
上面的while循环输出结果:
{ value: 'zhangsan', done: false }
{ value: 'lisi', done: false } 
{ value: 'hello', done: false }
*/


 

你可能感兴趣的:(es6,JavaScript,前端,es6,前端,javascript)