前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Iterator 、Generator

Iterator 、Generator

作者头像
用户3258338
发布2019-08-13 15:27:33
5670
发布2019-08-13 15:27:33
举报

爱是天时地利的迷信---《原来你也在这里》

宝宝们,周末过得充实有趣吗?今天上午下雨了,时小时大,雨声很好听~

speak is cheap ~

一、Iterator

Iterator(遍历器)的概念

JS里原有的表示”集合“的数据结构,主要是Array和Object,ES6又添加了Map和Set。我们可以任意组合和设计数据的结构,那么就需要一个机制,可处理所有不同的数据结构。

Iterator就是这个用途,他是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据只要不输Iterator接口,就可以完成遍历操作(依次处理该数据结构的所有成员)。

Iterator作用

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

Iterator的遍历过程

  1. 创建一个指针对象,指向当前数据结构的起始位置。遍历器对象本质就是一个指针对象(遍历器对象)
  2. 第一次调用指针对象的next方法,可以将 数据结构的第一个成员
  3. 第二次调用指针对象的next方法,指针就指向数据结构的第二个成员
  4. 不断调用指针对象的next方法,直到它指向数据结构的结束位置

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

Iterator

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

数据结构只要部署了Iterator接口,我们就称这种数据结构是”可遍历的“

ES6规定,默认的Iterator接口部署在数据结构的Symbol.iterator属性,Symbol.iterator属性本身是一个函数,是当前数据结构默认的遍历器生成函数。执行这个函数就返回一个遍历器。

上面的话头大吗?还是看代码吧!

代码语言:javascript
复制
const obj = {
  [Symbol.iterator] : function () {
    return {
      next: function () {
        return {
          value: 1,
          done: true
        };
      }
    };
  }
};

ES6的某些数据结构原生部署了Symbol.iteraotr属性,即不用任何处理就可以被for...of循环遍历。另外一些数据结构没有。

原生具备Iterator接口的数据结构:

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的arguments对象
  • NodeList对象
代码语言:javascript
复制
let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();

iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }

调用Iterator接口的场合

有些场合会默认调用Iterator接口,如下:

1.解构赋值

对数组和Set结构解构赋值

代码语言:javascript
复制
let set = new Set().add('a').add('b').add('c');

let [x,y] = set;
// x='a'; y='b'

let [first, ...rest] = set;
// first='a'; rest=['b','c'];

2.扩展运算符

扩展用算符(...)也会调用默认的Iterator接口

代码语言:javascript
复制
// 例一
var str = 'hello';
[...str] //  ['h','e','l','l','o']

// 例二
let arr = ['b', 'c'];
['a', ...arr, 'd']
// ['a', 'b', 'c', 'd']

3.yield*

yield*后面跟的是一个可遍历的结构,他会调用该结构的遍历器接口

代码语言:javascript
复制
let generator = function* () {
  yield 1;
  yield* [2,3,4];
  yield 5;
};

var iterator = generator();

iterator.next() // { value: 1, done: false }
iterator.next() // { value: 2, done: false }
iterator.next() // { value: 3, done: false }
iterator.next() // { value: 4, done: false }
iterator.next() // { value: 5, done: false }
iterator.next() // { value: undefined, done: true }

4.其他场合

任何接受数组最为参数的场合,其实都调用了遍历器接口:

  • for...of
  • Array.form()
  • Map(),Set(),WeakMap(),WeakSet()
  • Promise.all()
  • Promise.race()

Iterator 接口最简单的实现

Symbol.iterator方法最简单实现,还是使用下一章介绍的Generator函数

代码语言:javascript
复制
let myIterable ={
  [Symbol.iterator]:function*(){
    yield 1;
    yield 2;
    yield 3;
  }
}
[...myIterable] //[1,2,3]
//更简洁写法
let obj = {
  *[Symbol.iterator](){
    yield 'hello';
    yield 'world';
  }
}
for(let x of obj){
  console.log(x)
}
// 'hello'
// 'world'

与其他遍历语法的比较

  • for循环: 写法比较麻烦
  • forEach 写法简单,但是无法跳出forEach循环,break和return都不奏效
  • for...in 遍历数组的键名。缺点 - 数组键名是数字,但是for...in循环是以字符串作为键名”0“,”1“,”2“等 - 不仅遍历数字键名,还会遍历手动添加的其他键,设置包括原型上的键 - 某些情况下,for...in循环会以任意顺序遍历键名
  • for...of循环优点 -语法简洁 -可以与break、continue、return配合使用 - 可遍历所有数据结构

二、Generotor

Generator的概念

Generator函数是ES6提供的一种异步编程解决方案,语法行为和传统函数完全不同。

在语法上,Generator函数是一个状态机,封装了多个内部状态。

执行Generator函数会返回一个遍历器对象,也就是说,他还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历Generator函数内部的每一个状态。

形式上,Generator函数是一个普通函数,有两个特征:

  1. function和函数名之间有一个星号
  2. 函数体内部使用yield(产出)表达式,定义不同的内部状态
代码语言:javascript
复制
function* helloWorldGenerator(){
  yield 'hello';
  yield 'world';
  return 'ending';
}
var hw = helloWorldGenerator();

Generator函数调用与普通函数一样,但是,调用Generator函数后,该函数并不执行。返回的也不是函数运行结果,而是一个指向内部状态的指针对象(遍历器对象,Iterator对象);

必须调用遍历器对象的next方法,使得指针移向下一个状态。每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止。即,yield是分段执行的,yield表达式是暂停执行的标志,而next方法可以恢复执行。

代码语言:javascript
复制
hw.next()
// { value: 'hello', done: false }

hw.next()
// { value: 'world', done: false }

hw.next()
// { value: 'ending', done: true }

hw.next()
// { value: undefined, done: true }

未完待续……

愿我们有能力不向生活缴械投降---Lin

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-08-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 女程序员的日常 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档