1.迭代器,可迭代,生成器基础教程

1.迭代协议

 可迭代协议

可迭代协议允许 JavaScript 对象去定义或定制它们的迭代行为, 例如(定义)在一个 for..of 结构中什么值可以被循环(得到)。一些内置类型都是内置的可迭代类型并且有默认的迭代行为, 比如 Array or Map, 另一些类型则不是 (比如Object) 。

为了变成可迭代对象, 一个对象必须实现 @@iterator 方法, 意思是这个对象(或者它原型链 prototype chain 上的某个对象)必须有一个名字是 Symbol.iterator 的属性:

属性
[Symbol.iterator]返回一个对象的无参函数,被返回对象符合迭代器协议。

当一个对象需要被迭代的时候(比如开始用于一个for..of循环中),它的@@iterator方法被调用并且无参数,然后返回一个用于在迭代中获得值的迭代器。 

迭代器协议

迭代器协议定义了一种标准的方式来产生一个有限或无限序列的值,并且当所有的值都已经被迭代后,就会有一个默认的返回值。

当一个对象只有满足下述条件才会被认为是一个迭代器:
它实现了一个 next() 的方法并且拥有以下含义:

属性
next

返回一个对象的无参函数,被返回对象拥有两个属性:

  • done (boolean)
    • true:迭代器已经超过了可迭代次数。这种情况下,value的值可以被省略
    • 如果迭代器可以产生序列中的下一个值,则为 false。这等效于没有指定done这个属性。
  • value - 迭代器返回的任何 JavaScript 值。done 为 true 时可省略。

next 方法必须要返回一一个对象,该对象有两个必要的属性: done和value,如果返回一个非对象值(比如false和undefined) 会展示一个 TypeError ("iterator.next() returned a non-object value") 的错误

不可能知道一个特定的对象是否实现了迭代器协议,然而创造一个同时满足迭代器协议和可迭代协议的对象是很 容易的(就像下面的example所示)。这样做允许一个迭代器能被不同希望迭代的语法方式所使用。 因此,很少只实现迭代器协议而不实现可迭代协议。

 

标准的实现了迭代器协议和可迭代协议的

var myIterator = {
    next: function() {
        // ...
    },
    [Symbol.iterator]: function() { return this }
}

 

一些内置的语法结构,比如 spread operator (展开语法:[...val]),内部也使用了同样的迭代协议

[...someString]                              // ["h", "i"]

 

一些迭代器是转换自可迭代对象.

var someArray = [1, 5, 7];
var someArrayEntries = someArray.entries();

someArrayEntries.toString();           // "[object Array Iterator]"
someArrayEntries === someArrayEntries[Symbol.iterator]();    // true

我们可以通过自己的 @@iterator 方法重新定义迭代行为:

var someString = new String("hi");          // need to construct a String object explicitly to avoid auto-boxing

someString[Symbol.iterator] = function() {
  return { // this is the iterator object, returning a single element, the string "bye"
    next: function() {
      if (this._first) {
        this._first = false;
        return { value: "bye", done: false };
      } else {
        return { done: true };
      }
    },
    _first: true
  };
};

可迭代对象

内置可迭代对象

StringArrayTypedArrayMap and Set 是所有内置可迭代对象, 因为它们的原型对象都有一个 @@iterator 方法.

自定义可迭代对象

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

接受可迭代对象的内置 API

许多 API 接受可迭代对象(作为参数,译注), 例如:Map([iterable])WeakMap([iterable])Set([iterable]) and WeakSet([iterable]): 另外还有 Promise.all(iterable)Promise.race(iterable) 以及 Array.from().

var myObj = {};
new Map([[1,"a"],[2,"b"],[3,"c"]]).get(2);               // "b"
new WeakMap([[{},"a"],[myObj,"b"],[{},"c"]]).get(myObj); // "b"
new Set([1, 2, 3]).has(3);                               // true
new Set("123").has("2");                                 // true
new WeakSet(function*() {
    yield {};
    yield myObj;
    yield {};
}()).has(myObj);                                         // true

用于可迭代对象的语法

一些语句和表达式是预料会用于可迭代对象,比如 for-of 循环,spread operatoryield* 和 destructuring assignment

for(let value of ["a", "b", "c"]){
    console.log(value);
}
// "a"
// "b"
// "c"

[..."abc"]; // ["a", "b", "c"]

function* gen(){
  yield* ["a", "b", "c"];
}

gen().next(); // { value:"a", done:false }

[a, b, c] = new Set(["a", "b", "c"]);
a // "a"

 生成器对象到底是一个迭代器还是一个可迭代对象?

生成器对象 既是迭代器也是可迭代对象:

就像前面所说的,一个良好的迭代即实现了迭代器协议,又实现了可迭代协议,方式就是可迭代协议返回的是自身.

var aGeneratorObject = function*(){
    yield 1;
    yield 2;
    yield 3;
}();
typeof aGeneratorObject.next;
// "function", because it has a next method, so it's an iterator
typeof aGeneratorObject[Symbol.iterator];
// "function", because it has an @@iterator method, so it's an iterable
aGeneratorObject[Symbol.iterator]() === aGeneratorObject;
// true, because its @@iterator method return its self (an iterator), so it's an well-formed iterable
[...aGeneratorObject];
// [1, 2, 3]

2.for...of

 for...of语句可迭代对象(包括 ArrayMapSetStringTypedArrayarguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句.

for (variable of iterable) {
    //statements
}

variable

在每次迭代中,将不同属性的值分配给变量。

iterable

被迭代枚举其属性的对象。

 

 

3.function* 和 Generator

generator

生成器对象是由一个 generator function 返回的,并且它符合可迭代协议迭代器协议

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

let g = gen(); 
// "Generator { }"

 

方法
Generator.prototype.next()
返回一个由 yield表达式生成的值。
Generator.prototype.return()
返回给定的值并结束生成器。
Generator.prototype.throw()
向生成器抛出一个错误。

关于generator中的next() 方法传值 其中value是next()中上传的参数.

1. value 2.yield 将获得next 调用函数中的值 3. next()

 

function*

 

 这种声明方式(function关键字后跟一个星号)会定义一个生成器函数 (generator function),它返回一个  Generator  对象。 

你也可以使用构造函数  GeneratorFunction 或 function* expression 定义生成器函数 

function* name([param[, param[, ... param]]]) { statements }
name
函数名
param
要传递给函数的一个参数的名称,一个函数最多可以有255个参数。
statements
普通JS语句。

 

4.yield 和 yield*

 

[rv] = yield [expression];

 

expression

定义通过迭代器协议从生成器函数返回的值。如果省略,则返回undefined

rv

返回传递给生成器的next()方法的可选值,以恢复其执行。

yield关键字使生成器函数执行暂停,yield关键字后面的表达式的值返回给生成器的调用者。它可以被认为是一个基于生成器的版本的return关键字。

yield关键字实际返回一个IteratorResult对象,它有两个属性,valuedonevalue属性是对yield表达式求值的结果,而donefalse,表示生成器函数尚未完全完成。

一旦遇到 yield 表达式,生成器的代码将被暂停运行,直到生成器的 next() 方法被调用。每次调用生成器的next()方法时,生成器都会恢复执行,直到达到以下某个值:

  • yield,导致生成器再次暂停并返回生成器的新值。 下一次调用next()时,在yield之后紧接着的语句继续执行。
  • throw用于从生成器中抛出异常。这让生成器完全停止执行,并在调用者中继续执行,正如通常情况下抛出异常一样。
  • 到达生成器函数的结尾;在这种情况下,生成器的执行结束,并且IteratorResult给调用者返回undefined并且donetrue
  • 到达return 语句。在这种情况下,生成器的执行结束,并将IteratorResult返回给调用者,其值是由return语句指定的,并且done 为true

如果将参数传递给生成器的next()方法,则该值将成为生成器当前yield操作返回的值。

在生成器的代码路径中的yield运算符,以及通过将其传递给Generator.prototype.next()指定新的起始值的能力之间,生成器提供了强大的控制力。

yield* [[expression]];
expression
返回一个可迭代对象的表达式。

yield* 表达式迭代操作数,并产生它返回的每个值。

yield* 表达式本身的值是当迭代器关闭时返回的值(即donetrue时)。

function* g3() {
  yield* [1, 2];
  yield* "34";
  yield* arguments;
}

var iterator = g3(5, 6);

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

yield* 是一个表达式,不是语句,所以它会有自己的值。

function* g4() {
  yield* [1, 2, 3];
  return "foo";
}

var result;

function* g5() {
  result = yield* g4();
}

var iterator = g5();

console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }, 
                              // 此时 g4() 返回了 { value: "foo", done: true }

console.log(result);          // "foo"

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值