Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Iterator(迭代器) #38

Open
18888628835 opened this issue Jun 8, 2021 · 0 comments
Open

Iterator(迭代器) #38

18888628835 opened this issue Jun 8, 2021 · 0 comments

Comments

@18888628835
Copy link
Owner

18888628835 commented Jun 8, 2021

Iterator(迭代器)

什么是Iterator

可迭代(Iterable) 对象是数组的泛化。这个概念是说任何对象都可以被定制为可在 for..of 循环中使用的对象。

数组是可迭代的。但不仅仅是数组。很多其他内建对象也都是可迭代的。例如字符串也是可迭代的。

Iterator 就是针对那么多可迭代的数据结构中是一种统一的接口机制,只要部署了这个接口,就可以被依次遍历处理。

Iterator 的遍历过程是这样的:

  • 首先会在内部创建一个指针,指向指针开始位置
  • 当循环开始时,调用其内部的 next 方法
  • 返回当前成员信息=>{value:any,done:boolean}
  • 第二步-第三步循环执行,直到指针指到最后位置
  • 结束

手写 Iterator

下面我们通过一个例子快速了解核心概念。

需要知道的是,对象默认是没有 Iterator 接口的,所以它不能被 for..of 循环。这里我们手动给它设置 Iterator 接口。

let range={
  from:0,
  to:5
}

如果能让上面的range对象通过for..of循环打印出0、1、2、3、4、5。这就代表 iterator 接口部署成功了

1.首先我们要给它设置一个属性,这个属性只能通过Symbol.iterator访问,这个属性保存一个函数。

range[Symbol.iterator]=function(){}

这个 Symbol.iterator是部署的开始,当 for..of 开始循环时,会调用Symbol.iterator函数
2.Symbol.iterator函数会返回一个对象,内部有一个 next 方法,并返回{value:xxx,done:boolean}这样的对象。

// 由for...of 调用range[Symbol.iterator]()并返回 Iterator 对象
range[Symbol.iterator]=function(){
  // 返回一个iterator 对象
  return {
    first:0,
    last:5,
    next(){
    //这里的 this 指向iterator 对象,由 for..of 每次循环时自动调用该对象的 next 方法
      if(this.first<=this.last){
        return {value:this.first++,done:false}
      }else{
        return {done:true}
      }
    }
  }
}

3.打印信息

for(let i of range){
  alert(i)
}

会依次打出0-5的数,Iterator接口部署成功了。

小结

如果需要部署 Iterator 接口,我们需要手动添加 Symbol.Iterator属性。

这个属性保存着一个函数,当 for..of 开始时,就会调用该函数,并返回Iterator 对象,此时指针初始化。

Iterator对象内部保存着一个 next 函数,for..of 的每次循环都会调用到Iterator对象中的next 函数,所以这时候的 this 指向 Iterator 对象。

next函数会返回{value:xx,done:boolean}的数据结构。 当 done 为 true 时,循环停止。

for..of循环每次都会取返回结果的value属性

内置 Iterator

数组跟字符串内置了 Iterator 接口,所以我们可以直接使用 for..of来循环,比如下面的字符串

for(let str of 'strings'){
  console.log(str)
}
// s t r i n g s

下面我们直接获取它的Symbol.iterator属性,来看看到底发生了什么。

我们已经知道这个属性保存着一个函数,这个函数会返回 Iterator 对象,所以我们直接调用拿到这个对象。

let strings='strings'
let Iterator=strings[Symbol.iterator]()

这个对象内部会执行 next 方法,返回{value:xx,done:boolean}对象

Iterator.next()
//{value: "s", done: false}

现在我们得出一个结论:可迭代对象就是内部部署了 Iterator接口的对象,这个部署 Iterator 接口的标志是内部有 [Symbol.iterator]属性。

我们可以通过浏览器打印出来看一下,比如例子中的strings 的原型上就有[Symbol.iterator]属性,它是一个函数。

image.png

我们直接调用这个方法,然后看看内部到底是个啥

image.png
可以看到返回的StringIterator 的原型上有个 next 方法。

伪数组问题

可迭代对象的概念就是实现[Symbol.iterator]方法的对象

伪数组和数组一样,本质上是个对象,但是数组有数组原型(Array.prototype),而伪数组没有数组原型。

通过上面的截图我们知道[Symbol.iterator]会部署在原型上,比如数组原型、字符串原型里就有部署这个属性。

对象以及其原型是没有部署 Iterator 接口的。伪数组既然没有数组的原型,那就不一定有 Iterator 接口。

比如下面是一个伪数组

let arrayLike = { // 有索引和 length 属性 => 类数组对象
  0: "Hello",
  1: "World",
  length: 2
};

它的原型直接连接到 Object.prototype 上,所以它没有数组的共用方法。比如 pushpop 等等。

如果希望实现,那可以采用Array.from

Array.from(arrayLike)

这样这个伪数组就具备 Iterator 接口了。

有没有一种伪数组虽然没有数组的原型,但是却默认部署 Iterator 接口的呢?

有的,任何可以被扩展运算符变成数组的伪数组都有Iterator接口。

比如arguments虽然没有数组的原型,但是却具有 [Symbol.iterator] 接口

image.png

扩展运算符把伪数组变成数组?

也许你会认为扩展运算符能把所有伪数组变成数组,不过这只对了一半。

扩展运算符只能把已经具备 Iterator 接口的数据变成数组。不信我们看

image.png

结论就是只要你部署了Iterator 接口,才能用扩展运算符转化为数组。可不要弄反顺序噢。

总结

Iterator 是一个接口,具备 Iterator 接口的特点是内部有[Symbol.iterator]属性。这个属性保存着一个会返回 Iterator 对象的函数。

Iterator对象内部具备 next 方法,会返回键名为 done 和 value 的对象。

for..of 遍历器为部署了 Iterator 接口的数据结构而生,当使用 for..of 遍历时,会首先调用[Symbol.iterator] 方法,生成Iterator对象,每次循环都会调用这个对象内部的 next 方法。

有一些伪数组没有部署Iterator 接口,所以不能被扩展运算符转化为数组。这时候需要采用 Array.from方法。

Symbol.iterator 方法会被 for..of 自动调用,但我们也可以直接调用它。

内置的可迭代对象例如字符串和数组,都实现了 Symbol.iterator。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant