Iterator

1、iterator迭代器是什么

js原有的表示数据的集合主要是数组和对象,而es6又新增了map和set用于表示新的数据结构,而用户又可以在内部自定义自己的数据结构,这是就需要一个接口来方便我们可以对他进行统一的遍历。

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

2、iterator的作用

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

3、遍历过程

  1. 创建一个指针对象,指向当前数据结构的起始位置,也就是说,遍历器对象事实上是一个指针对象
  2. 第一次调用next方法,可以将指针指向数据结构的第一个成员,以此类推
  3. 不断调用next方法,直到他指向数据结构的结束位置

每一次调用next方法,都会返回数据结构的当前成员信息。具体来说,就是返回以恶搞包含value和done两个属性的对象,其中value是当前成员值,done是表示是否结束

模拟:

let it = makeIterator([1, 2, 3])

function makeIterator(arr) {
    let index = 0
    return {
        next() {
            return {
                value: index < arr.length ? arr[index++] : undefined,
                done: index < arr.length ? false : true
            }

        }
    }
}


console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());

4、默认iterator接口

Iterator 接口的目的,就是为所有数据结构,提供了一种统一的访问机制, 即 for…of 循环。当使用 for…of 循环遍历某种数据结构时, 该循环会自动去寻找 Iterator 接口。

es6规定,默认的iterator接口部署在数据结构的symbol.iterator属性上,有这个属性的数据结构称为可遍历的,symbol.iterator本身是一个函数,类似上面我们模拟的函数,执行这个函数会返回一个遍历器

原生部署了iterator接口的数据结构:

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的arguments对象
  • NodeList 对象

注意,对象没有

字符串是一个类数组对象,因此也原生具有iterator接口

对一个数据结构使用了iterator,事实上是将他变成了一个线性遍历的数据,因为iterator是一直以next的方法来遍历,无法乱序遍历

为类添加遍历器:

class rangeIterator {
  constructor(start, stop) {
    this.start = start
    this.stop = stop
  }

  [Symbol.iterator]() {
    return this
  }

  next() {
    let cur = this.start
    if (cur < this.stop) {
      this.start++
        return { value: cur }
    } else {
      return { done: true }
    }
  }
}

function range(start, stop) {
  return new rangeIterator(start, stop)
}

for (const iterator of range(0, 3)) {
  console.log(iterator)
}

为对象的prototype属性添加迭代器

function Obj(value) {
  this.value = value
  this.next = null
}

Obj.prototype[Symbol.iterator] = function() {

  let iterator = { next: next }
  let that = this

  function next() {
    if (that) {
      let value = that.value
      that = that.next
      return { value: value }
    } else {
      return { done: true }
    }
  }

  return iterator
}

var one = new Obj(1);
var two = new Obj(2);
var three = new Obj(3);
one.next = two;
two.next = three;
for (var i of one) {
  console.log(i); // 1, 2, 3
}

牢牢把握住,for-of方法是调用了[Symbol.ierator]这个属性的方法,然后返回了一个迭代器,然后调用迭代器的next方法来遍历

对于类数组对象,可以直接将数组的iterator方法给他

NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.ite
rator];
// 或者
NodeList.prototype[Symbol.iterator] = [][Symbol.iterator];

5、调用iterator接口的场合

除了for…of外有些场合,会默认调用iterator接口

  1. 对set和array解构赋值时

  2. 使用扩展运算符时

    等于说,只要有iterator接口,就可以使用扩展预算符将他变成数组

  3. yield*

  4. 因为遍历数组会调用该接口,因此相当于只要使用了数组作为参数的场合,就是调用了

6、iterator与generator结合使用

7、遍历器对象的return(),throw()

如果是自己写的遍历器方法,可以省略这两个方法

return()方法一般用于以下情况:;

// 情况一
for (let line of readLinesSync(fileName)) {
    console.log(line);
    break;
}
// 情况二
for (let line of readLinesSync(fileName)) {
    console.log(line);
    continue;
}
// 情况三
for (let line of readLinesSync(fileName)) {
    console.log(line);
    throw new Error();
}

而throw方法一般用于配合generator函数使用,一般遍历器用不到这个方法

8、for…of…方法

该方法调用的是iterator接口,具有该接口的对象都称为可遍历对象

js原有的for…in…方法只能遍历到数组的键,而不能直接遍历到数组的值,for…of…方法只能直接遍历到数组的值,而不能遍历到数组的键

要想for…of…能够遍历到数组的键,需要使用entries和keys方法

9、关于其他数据结构

计算生成的数据结构

有些数据结构是在现有数据结构的基础上,计算生成的。比如,ES6的数组、Set、 Map 都部署了以下三个方法,调用后都返回遍历器对象。

  • entries() 返回一个遍历器对象,用来遍历 [键名, 键值] 组成的数组。对 于数组,键名就是索引值;对于 Set,键名与键值相同。Map 结构的 Iterator 接口,默认就是调用 entries 方法。
  • keys() 返回一个遍历器对象,用来遍历所有的键名。
  • values() 返回一个遍历器对象,用来遍历所有的键值。

这三个方法调用后生成的遍历器对象,所遍历的都是计算生成的数据结构。

计算生成的数据结构也可以进行遍历

类数组对象

类数组对象也可以使用for…of…进行遍历,最常见的是获取节点nodeList,字符串

// 字符串
let str = "hello";
for (let s of str) {
console.log(s); // h e l l o
}

但也不是所有的类数组对象都具有iterator接口,可以我们可以使用Array.from将该对象转成数组

对象

for…of…循环不能直接遍历对象,但是可以使用Object.keys来拿到键名的数组进行遍历然后取值,这种情况下for…in…可以直接拿到键名,会更加方便

10、与其他语法比较

原始for循环

优点:可以方便的拿到数组下标

缺点:写起来麻烦

forEach循环

优点:方便,可以拿到下标和对应值

缺点:无法中途跳出循环,break和return命令失效

forin循环

优点:可遍历数组的键名

缺点:

  • 数组的键名是数字,但是 for…in 循环是以字符串作为键名“0”、“1”、“2”等 等。

  • for…in 循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原 型链上的键。

  • 某些情况下, for…in 循环会以任意顺序遍历键名。

    let obj = {
      '21': '21',
      '12': '12'
    }
    
    for (const key in obj) {
      console.log(obj[key]);
    }
    
    // 12 21
    

总之, for…in 循环主要是为遍历对象而设计的,不适用于遍历数组。

forof循环

优点:

  • 有forin方法的简洁
  • 没有forEach方法的缺点
  • 提供了遍历所有书基本解构的统一操作接口
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值