迭代器和可迭代对象

迭代器

认识什么叫迭代器

维基百科对迭代器的定义:迭代器是确使用户在容器对象上遍访的对象,使用该接口无需关心对象内部实现的细节,维基百科的解释确实很正确,但是他喜欢用概念解释概念,所以说也不好理解。我觉得用大白话来形容:就是迭代器可以帮助我们对某个数据结构进行遍历的对象,在很多语言都有,比如说java、python…

JavaScript中迭代器

  1. 在JavaScript中迭代器也是一个具体的对象,不过这个对象需要符合迭代器协议(iterator protocol)。迭代器协议定义了一系列的值产生的标准。

    MDN中文文档
    如果我们阅读英文文档会发现这个位置其实中文翻译的是有问题的,下面这副图才是正确解释

    MDN英文文档

    1
    2
    let arr = [1,2,3,4,5]
    //符合迭代器协议的对象,可以通过某个标准拿到arr数组中的一个又一个的项,在js中这个标准就是一个特定的next方法
  2. next方法也不是随便定义的一个方法,他也有一定的要求

    • 他是一个无参数或者一个参数的函数,返回一个应当拥有以下两个属性的对象(done、value)
    • 当done的值为false的时候,就代表迭代器可继续产出值,当迭代完毕之后就返回一个true(迭代结束的时候true可以省略)
    • value就是返回false之后需要给使用者返回的值,是最直观的提现,使用者可以拿到这个值进行一系列的操作,当迭代完毕的时候done的值为true,value的值是可选的,如果它存在那么就作为迭代器结束之后的默认值返回。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    const arr = [1, 2, 3, 4, 5];
    let index = 0;
    const iterableArr = {
    next() {
    if (index < arr.length) {
    return { done: false, value: arr[index++] };
    } else {
    return { done: true, value: undefined };
    }
    },
    };
    console.log(iterableArr.next()); //{ done: false, value: 1 }
    console.log(iterableArr.next()); //{ done: false, value: 2 }
    console.log(iterableArr.next()); //{ done: false, value: 3 }
    console.log(iterableArr.next()); //{ done: false, value: 4 }
    console.log(iterableArr.next()); //{ done: false, value: 5 }
    console.log(iterableArr.next()); //{ done: true, value: undefined }
    console.log(iterableArr.next()); //{ done: true, value: undefined }

    //这样我们就实现了一个简易的迭代器

可迭代对象

认识什么叫可迭代对象

​ 可迭代对象和迭代器完全是两个概念,不能混淆了,可迭代对象需要实现(adorably protocol协议)

并且返回一个迭代器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const iterableObj = {
arr: [1, 2, 3, 4, 5],
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
if (index < this.arr.length) {
return { done: false, value: this.arr[index++] };
} else {
return { done: true };
}
},
};
},
};

for (const item of iterableObj) {
console.log(item); //1,2,3,4,5
}

迭代器这种代码,通过调用next去获取数组的每一项,我们在开发中是很少写的,但是这种for..of的代码在开发中是很常见的,for..of可以看做这种语法的语法糖。

可迭代对象的应用

  1. 可以应用于for..of遍历

  2. 也可以引用到展开运算符中

    • 如果是一个对象,那么也是可以的,那么就有一个问题,for..of遍历对象的时候是不可以的,会报错的,例子:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      let obj = {
      name: "coderwei",
      age: 19,
      };

      let newObj = { ...obj };
      console.log(newObj); //{name:'coderwei',age:19}
      for (const item of Obj) {
      console.log(item); //报错
      }
      //展开运算符用在一个对象上原理不是利用可迭代对象,这时ES9提出来的,对对象使用的时候会做特殊的处理,而且展开运算符只能将对象展开到另一个对象。如果展开到数组里依旧会报错:xxx is not iterable
  3. 解构

    • 当我们使用解构赋值的时候,本质上也是利用可迭代对象的特性,当前同样要区分解构对象和数组

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      //解构数组
      let arr = [1,2,3]

      const [x ,y ,z ] = arr
      console.log(x,y,z) //1,2,3
      const [x ,z ,y] = arr
      console.log(x,y,z) // 1,3,2

      //解构对象
      let obj = {name:'coderwei',age:19}

      const {name,age} = obj
      console.log(name.age) //'coderwei',19
      const {obj,name} = obj
      console.log(name.age) //'coderwei',19

      //我们可以发现当我们解构对象的时候,跟定义变量的顺序是无关的,而解构数组则是有关的,也证明了解构数组只是调用迭代器的next方法拿到一个个的value,然后将它赋值给定义的变量

封装一个创建迭代器的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function createIterator(arr){
let index = 0
return {
next:function(){
if(index<arr.length){
return {done:false,value:arr[index++]}
}else{
return {done:true,value:undefined}
}
}
}
}



let arr = [1, 2, 3, 4, 5];
let itor = createIterator(arr);
console.log(itor.next()); //{ done: false, value: 1 }
console.log(itor.next()); //{ done: false, value: 2 }
console.log(itor.next()); //{ done: false, value: 3 }
console.log(itor.next()); //{ done: false, value: 4 }
console.log(itor.next()); //{ done: false, value: 5 }
console.log(itor.next()); //{ done: true, value: undefined }
console.log(itor.next()); //{ done: true, value: undefined }