ES6之Iterator、Generator

Iterator的作用

一是为种种数据构造,供应一个一致的、轻便的接见接口;(一致)
二是使得数据构造的成员能够按某种序次分列;(顺次
三是ES6制造了一种新的遍历敕令for…of轮回,Iterator接口重要供for…of消耗。
举个例子:遍历器天生函数,作用就是返回一个遍历器对象,next要领返回一个对象,示意当前数据成员的信息。这个对象具有value和done两个属性,value属性返回当前位置的成员,done属性是一个布尔值,示意遍历是不是完毕

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属性都是能够省略的,所以上述代码能够简写为:

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

Iterator 只是把接口规格加到数据构造之上,所以,遍历器与它所遍历的谁人数据构造,实际上是离开的,完全能够写出没有对应数据构造的遍历器对象,或许说用遍历器对象模拟出数据构造

Iterator接口

默许的 Iterator 接口布置在数据构造的Symbol.iterator属性,Symbol.iterator属性本身是一个函数,就是当前数据构造默许的遍历器天生函数。实行这个函数,就会返回一个遍历器
原生具有 Iterator 接口的数据构造以下:
1)Array
2)Map
3)Set
4)String
5)TypedArray
6)函数的 arguments 对象
关于布置了Iterator接口的数据构造除了能够运用for of轮回以外,能够用while推断对象的done属性举行轮回遍历
如何运用原生的遍历器呢?

let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();

iter.next()

//第二个例子:
var someString = "hi";
typeof someString[Symbol.iterator]
// "function"

var iterator = someString[Symbol.iterator]();

iterator.next()  // { value: "h", done: false }

关于相似数组的对象应当如何挪用数组的Symbol.iterator要领?

let iterable = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3,
  [Symbol.iterator]: Array.prototype[Symbol.iterator]//这句话是重点
};
for (let item of iterable) {
  console.log(item); // 'a', 'b', 'c'
}

运用Iterator的场景

解构赋值

let set = new Set().add('a').add('b').add('c');
let [first, ...rest] = set;
// first='a'; rest=['b','c'];
//对数组和 Set 构造举行解构赋值时,会默许挪用Symbol.iterator要领

扩大运算符

var str = 'hello';
[...str] //  ['h','e','l','l','o']
//扩大运算符(...)也会挪用默许的 Iterator 接口

yield*

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

var iterator = generator();

iterator.next()

遍历器的return和throw要领

遍历器对象天生函数,next要领是必需布置的,return要领和throw要领是不是布置是可选的。return要领必需返回一个对象

function readLinesSync(file) {
  return {
    next() {
      if (file.isAtEndOfFile()) {
        file.close();
        return { done: true };
      }
    },
    return() {
      file.close();
      return { done: true };
    },
  };
}
for (let line of readLinesSync(fileName)) {
  console.log(line);
  break;//我们让文件的遍历提早返回,如许就会触发实行return要领
}

for of 轮回长处

forEach没方法跳出轮回,也就是说在forEach当中break敕令或return敕令都不能见效
for…in轮回不仅遍历数字键名,还会遍历手动增加的其他键,以至包括原型链上的键,而且for in 是没有递次的(也就是说for…in轮回重假如为遍历对象而设想的,不适用于遍历数组)

小窍门:
1、并非一切相似数组的对象都具有 Iterator 接口,一个轻便的处置惩罚要领,就是运用Array.from要领将其转为数组

let arrayLike = { length: 2, 0: 'a', 1: 'b' };

// 报错
for (let x of arrayLike) {
  console.log(x);
}

// 准确
for (let x of Array.from(arrayLike)) {
  console.log(x);
}

2、因为一般对象并没有布置Iterator接口,所以是没法运用for of轮回的,用以下两种计划处置惩罚:

for (var key of Object.keys(someObject)) {
  console.log(key + ': ' + someObject[key]);
}

//第二种体式格局:
function* entries(obj) {
  for (let key of Object.keys(obj)) {
    yield [key, obj[key]];
  }
}
for (let [key, value] of entries(obj)) {
  console.log(key, '->', value);
}
//第三种体式格局:
function* objectEntries(obj) {
  let propKeys = Reflect.ownKeys(obj);
  for (let propKey of propKeys) {
    yield [propKey, obj[propKey]];
  }
}
for (let [key, value] of objectEntries(obj)) {
  console.log(`${key}: ${value}`);
}
//或许也能够如许
let jane = { first: 'Jane', last: 'Doe' };

jane[Symbol.iterator] = objectEntries;//这句话是重点将 Generator 函数加到对象的Symbol.iterator属性上面

for (let [key, value] of jane) {
  console.log(`${key}: ${value}`);
}

Generator简介

在我看来Generator就是为了异步编程供应一种处置惩罚计划;
Generator 函数是一个一般函数,事实上它是遍历器天生函数,但有几个特征:
1、function关键字与函数名之间有一个星号(星号紧跟在function关键字背面);
2、函数体内部运用yield表达式,定义差别的内部状况(yield在英语里的意义就是“产出” 1)yield只能在Generator函数中运用;2)yield表达式假如用在另一个表达式当中,必需放在圆括号内里;3)yield表达式用作函数参数或放在赋值表达式的右侧,能够不加括号);
3、能够把Generator函数理解为状况机,挪用 Generator 函数后,该函数并不实行,返回的也不是函数运转效果,而是一个指向内部状况的指针对象,每次挪用next要领,内部指针就从函数头部或上一次停下来的处所最先实行,直到碰到下一个yield表达式(或return语句)为止;Generator 函数是分段实行的,yield表达式是停息实行的标记,而next要领能够恢复实行。

var myIterable = {};
myIterable[Symbol.iterator] = function* () {
  yield 1;
  yield 2;
  yield 3;
};

[...myIterable]

Generator 函数实行后,返回一个遍历器对象。该对象本身也具有Symbol.iterator属性,实行后返回本身。

function* gen(){
  // some code
}

var g = gen();

g[Symbol.iterator]() === g

Generator中next要领的参数

经由过程next要领的参数,就有方法在 Generator 函数最先运转今后,继承向函数体内部注入值
因为next要领的参数示意上一个yield表达式的返回值,所以第一次运用next要领时,不能带有参数。V8 引擎直接疏忽第一次运用next要领时的参数,只要从第二次运用next要领最先,参数才是有用的
举个简朴的例子吧:

function* dataConsumer() {
  console.log('Started');
  console.log(`1. ${yield}`);
  console.log(`2. ${yield}`);
  return 'result';
}

let genObj = dataConsumer();
genObj.next();
// Started
genObj.next('a')
// 1. a
genObj.next('b')
// 2. b

庞杂一点的:

function* foo(x) {
  var y = 2 * (yield (x + 1));
  var z = yield (y / 3);
  return (x + y + z);
}

var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}

var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
//解释一下效果y=2*12 所以返回的是24/3
b.next(13) // { value:42, done:true }
//y=24 z=13 所以返回的是5+24+13

说说for of与Generator的关联吧

有一点须要注重的是当next要领中返回done为true则停止轮回且不包括该返回对象,所以上面代码的return语句返回的6,不包括在for…of轮回当中

function *foo() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 5;
  return 6;
}

for (let v of foo()) {
  console.log(v);
}

Generator中的throw要领

Generator 函数返回的遍历器对象,都有一个throw要领,能够在函数体外抛出毛病,然后在 Generator 函数体内捕捉,throw要领能够吸收一个参数,该参数会被catch语句吸收

var g = function* () {
  try {
    yield;
  } catch (e) {
    console.log(e);
  }
};

var i = g();
i.next();
i.throw(new Error('出错了!'));
// Error: 出错了!(…)

throw要领被捕捉今后,会附带实行下一条yield表达式。也就是说,会附带实行一次next要领。

var gen = function* gen(){
  try {
    yield console.log('a');
  } catch (e) {
    // ...
  }
  yield console.log('b');
  yield console.log('c');
}

var g = gen();
g.next() // a
g.throw() // b
g.next() // c

Generator中的throw有什么上风呢?多个yield表达式,能够只用一个try…catch代码块来捕捉毛病,大大方便了对毛病的处置惩罚。

一旦 Generator 实行过程当中抛出毛病,且没有被内部捕捉,就不会再实行下去了

function* g() {
  yield 1;
  console.log('throwing an exception');
  throw new Error('generator broke!');
  yield 2;
  yield 3;
}

如上所示代码:因为抛出了非常generator broke,所以背面的2 3都不会返回

Generator return

function* gen() {
  yield 1;
  yield 2;
  yield 3;
}

var g = gen();

g.next()        // { value: 1, done: false }
g.return('foo') // { value: "foo", done: true }
g.next()        // { value: undefined, done: true }

g挪用return要领后,返回值的value属性就是return要领的参数foo。而且,Generator函数的遍历就停止了,返回值的done属性为true,今后再挪用next要领,done属性老是返回true

别的一个特殊情况: Generator 函数内部有try…finally代码块,那末return要领会推晚到finally代码块实行完再实行。也就是说:挪用return要领后,就最先实行finally代码块,然后比及finally代码块实行完,再实行return要领

function* numbers () {
  yield 1;
  try {
    yield 2;
    yield 3;
  } finally {
    yield 4;
    yield 5;
  }
  yield 6;
}
var g = numbers();
g.next() // { value: 1, done: false }
g.next() // { value: 2, done: false }
g.return(7) // { value: 4, done: false }
g.next() // { value: 5, done: false }
g.next() // { value: 7, done: true }

yield*表达式

yield表达式背面跟的是一个遍历器对象,须要在yield表达式背面加上星号,表明它返回的是一个遍历器对象。这被称为yield*表达式

let delegatedIterator = (function* () {
  yield 'Hello!';
  yield 'Bye!';
}());

let delegatingIterator = (function* () {
  yield 'Greetings!';
  yield* delegatedIterator;
  yield 'Ok, bye.';
}());

for(let value of delegatingIterator) {
  console.log(value);
}
// "Greetings!
// "Hello!"
// "Bye!"
// "Ok, bye."

Generator函数中的this

Generator函数不能跟new敕令一升引
如何让Generator 函数返回一个一般的对象实例,既能够用next要领,又能够获得一般的this?

function* F() {
  this.a = 1;
  yield this.b = 2;
  yield this.c = 3;
}
//这是第一种体式格局
var obj = {};
var f = F.call(obj);//让F内部的this对象绑定obj对象

//第二种体式格局
var f = F.call(F.prototype);

f.next();  // Object {value: 2, done: false}
f.next();  // Object {value: 3, done: false}
f.next();  // Object {value: undefined, done: true}

obj.a // 1
obj.b // 2
obj.c // 3
    原文作者:angelayun
    原文地址: https://segmentfault.com/a/1190000010209382
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞