JS_iterator和for...of循环

Iterator(遍历器)

概念

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

作用
  1. 为各种数据结构,提供一个统一的。简便的访问接口;
  2. 使得数据结构的成员能够按照某种次序排列
  3. ES6创造了一种新的遍历命令for…of循环,Iterator接口主要提供for…of消费
遍历过程
  1. 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历对象本质上,就是一个指针对象
  2. 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员
  3. 第二次调用指针对象的next方法,指针就指向数据结构的第二个成员
  4. 不断调用指针对象的next方法,直到它指向数据结构的结束位置

每一次调用next方法,都会返回数据结构的当前成员的信息——返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。

例子
  //模拟next方法返回值的例子
  var it = makeIterator(['a','b'])

  let r1 = it.next()
  let r2 = it.next()
  let r3 = it.next()
  console.log(r1,r2,r3)

  function makeIterator(array){
    var nextIndex = 0;
    return {
      next: function() {
        return nextIndex < array.length ? {
          value: array[nextIndex++],done:false
        }:{
          value: undefined,
          done: true
        }
      }
    }
  }

在这里插入图片描述
对于遍历对象来说,done:false和value:undefined属性是可以省略的,因此可简写:

  //模拟next方法返回值的例子
  var it = makeIterator(['a','b'])

  let r1 = it.next()
  let r2 = it.next()
  let r3 = it.next()
  console.log(r1,r2,r3)

  function makeIterator(array){
    var nextIndex = 0;
    return {
      next: function() {
        return nextIndex < array.length ? {
          value: array[nextIndex++]
        }:{
          done: true
        }
      }
    }
  }

默认Iterator接口

一种数据结构只要部署了Iterator接口,就可以称这种数据结构使“可遍历的”(iterable)
ES6规定,默认的Iterator接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)
Symbol.iterable属性本身就是一个函数,就是当前数据结构默认的遍历器生成的函数。执行这个函数,就会返回一个遍历器。属性名Symbol.iterator是一个表达式,返回Symbol对象的iterator属性,这是一个预定好的,类型为Symbol的特殊值。例如下面这个obj

const obj = {
	[Symbol.iterator]: function (){
		return {
			next: function (){
			return {
			value: 1,
			done: true
			}
		}
		}
	}
	}

原生具备Iterator接口的数据结构:
array、map、set、string、typedArray、函数的arguments对象、nodeList对象
对于原生部署Iterator接口的数据结构,不用自己写遍历器生成函数,for…of循环会自动遍历它们。其他函数都要自己在Symbo.iterator属性上面部署,才可以被for…of循环遍历。(原型链上的对象具有该方法也可)

  class RangeIterator{
    constructor(start, stop){
      this.value =  start;
      this.stop = stop;
    }

    [Symbol.iterator]() {return this;}

    next(){
      var value = this.value;
      if(value < this.stop){
        this.value++;
        return {done: false,value:value};
      }

      return {done: true,value:undefined};
    }
  }
      function range(start,stop){
      return new RangeIterator(start, stop);
    }

    for(var value of range(0,3)){
      console.log(value)
    }

为对象添加Iterator接口

let obj = {
  data:['hello','world'],
  [Symbol.iterator](){
    const self = this;
    let index = 0;
    return {
      next() {
        if(index < self.data.length){
          return {
            value:self.data[index++],
            done:false
          };
        }else{
          return {value:undefined,done:true};
        }
      }
    }
  }
}
for (let item of obj){
  console.log(item)
}

对于类似数组的对象(存在数值键名和length属性),部署Iterator接口,有一个简便的方法——Symbol.Interator方法直接引用数组的Iterator接口

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

[...document.querySelectorAll('div')]//可以执行
  let iterable = {
    0: 'a',
    1:'b',
    2:'c',
    length:3,
    [Symbol.iterator]:Array.prototype[Symbol.iterator]
  };
  for(let item of iterable){
    console.log(item)
  }

NodeList对象是类似数组的对象,本身就具有遍历接口,可直接遍历。但上述代码将其遍历接口替换成Symbol。iterator属性,没有发生任何变化
对于普通对象,部署数组的Symbol。iterator方法无效果

调用Iterator接口的场合
  1. 解构赋值
    对数组和set结构进行解构赋值时,会默认调用Symbol.iterator方法
let set = new Set().add('a').add('b').add('c')
let [first,...rest] = set
console.log(first)
console.log(rest)

在这里插入图片描述
2. 扩展运算符
只要某个数据结构部署了Iterator接口,就可以对其使用扩展运算符,将其转为数组

var str = 'hello'
console.log([...str])

在这里插入图片描述
3. yield*
yield*后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口

  let generator = function* () {
    yield 1;
    yield* [2,3,4];
    yield 5;
  };
  var iterator = generator()

  console.log(iterator.next())
  console.log(iterator.next())
  console.log(iterator.next())
  console.log(iterator.next())
  console.log(iterator.next())
  console.log(iterator.next())

在这里插入图片描述
4. 其他场合
5. 由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口
例如:

  • for…of
  • Array.from()
  • Map() Set() WeakMap() WeakSet()
  • Promise.al()
  • Promise.race()
字符串的Iterator接口

字符串是一个类似数组的对象,也原生具有Iterator接口

  var someString = 'hi'
  console.log(typeof someString[Symbol.iterator])

  var iterator = someString[Symbol.iterator]()

  console.log(iterator.next())
  console.log(iterator.next())
  console.log(iterator.next())

在这里插入图片描述也可以覆盖原生的Symbol.iterator方法,达到修改遍历器行为的目的(log出来还是有点不一样)

var str = new String('hi')

console.log([...str])
console.log(str)

str[Symbol.iterator] = function(){
  return {
    next: function() {
      if(this._first){
        this._first = false;
        return {value:'bye',done:false};
      }else{
        return {done: true};
      }
    },
    _first:true
  }
}

console.log([...str])
console.log(str)

在这里插入图片描述

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

使用场合: 如果for…of循环提前退出(通常是因为出错,或者有break语句),就会调用return方法。如果一个对象在完成遍历前,需要清理或释放资源,就可以部署return方法。

for…of与array.forEach()

for…of可以用break、continue和return配合使用,但是forEach()不可以

for (var n of fibonacci) {
  if (n > 1000)
    break;
  console.log(n);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值