mirror of
https://github.com/ruanyf/es6tutorial.git
synced 2025-05-27 20:32:21 +00:00
edit generator && iterator
This commit is contained in:
parent
508fc8d624
commit
21e842bde6
@ -2,9 +2,13 @@
|
||||
|
||||
## 简介
|
||||
|
||||
所谓Generator,有多种理解角度。首先,可以把它理解成一个函数的内部状态的遍历器,每调用一次,函数的内部状态发生一次改变(可以理解成发生某些事件)。ES6引入Generator函数,作用就是可以完全控制函数的内部状态的变化,依次遍历这些状态。
|
||||
### 基本概念
|
||||
|
||||
在形式上,Generator是一个普通函数,但是有两个特征。一是,function命令与函数名之间有一个星号;二是,函数体内部使用yield语句,定义遍历器的每个成员,即不同的内部状态(yield语句在英语里的意思就是“产出”)。
|
||||
Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同。本章详细介绍Generator函数的语法和API,它的异步编程应用请看《异步操作》一章。
|
||||
|
||||
Generator函数有多种理解角度。从语法上,首先可以把它理解成一个函数的内部状态的遍历器(也就是说,Generator函数是一个状态机)。它每调用一次,就进入下一个内部状态。Generator函数可以控制内部状态的变化,依次遍历这些状态。
|
||||
|
||||
在形式上,Generator函数是一个普通函数,但是有两个特征。一是,function命令与函数名之间有一个星号;二是,函数体内部使用yield语句,定义遍历器的每个成员,即不同的内部状态(yield语句在英语里的意思就是“产出”)。
|
||||
|
||||
```javascript
|
||||
function* helloWorldGenerator() {
|
||||
@ -16,12 +20,13 @@ function* helloWorldGenerator() {
|
||||
var hw = helloWorldGenerator();
|
||||
```
|
||||
|
||||
上面代码定义了一个Generator函数helloWorldGenerator,它的遍历器有两个成员“hello”和“world”。调用这个函数,就会得到遍历器。
|
||||
上面代码定义了一个Generator函数helloWorldGenerator,它内部有两个yield语句“hello”和“world”,即该函数有三个状态:hello,world和return语句(结束执行)。
|
||||
|
||||
当调用Generator函数的时候,该函数并不执行,而是返回一个遍历器(可以理解成暂停执行)。以后,每次调用这个遍历器的next方法,就从函数体的头部或者上一次停下来的地方开始执行(可以理解成恢复执行),直到遇到下一个yield语句为止。也就是说,next方法就是在遍历yield语句定义的内部状态。
|
||||
然后,Generator函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是上一章介绍的遍历器对象(Iterator Object)。
|
||||
|
||||
下一步,必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield语句(或return语句)为止。换言之,Generator函数是分段执行的,yield命令是暂停执行的标记,而next方法可以恢复执行。
|
||||
|
||||
```javascript
|
||||
|
||||
hw.next()
|
||||
// { value: 'hello', done: false }
|
||||
|
||||
@ -33,46 +38,49 @@ hw.next()
|
||||
|
||||
hw.next()
|
||||
// { value: undefined, done: true }
|
||||
|
||||
```
|
||||
|
||||
上面代码一共调用了四次next方法。
|
||||
|
||||
第一次调用,函数开始执行,直到遇到第一句yield语句为止。next方法返回一个对象,它的value属性就是当前yield语句的值hello,done属性的值false,表示遍历还没有结束。
|
||||
第一次调用,Generator函数开始执行,直到遇到第一个yield语句为止。next方法返回一个对象,它的value属性就是当前yield语句的值hello,done属性的值false,表示遍历还没有结束。
|
||||
|
||||
第二次调用,函数从上次yield语句停下的地方,一直执行到下一个yield语句。next方法返回的对象的value属性就是当前yield语句的值world,done属性的值false,表示遍历还没有结束。
|
||||
第二次调用,Generator函数从上次yield语句停下的地方,一直执行到下一个yield语句。next方法返回的对象的value属性就是当前yield语句的值world,done属性的值false,表示遍历还没有结束。
|
||||
|
||||
第三次调用,函数从上次yield语句停下的地方,一直执行到return语句(如果没有return语句,就执行到函数结束)。next方法返回的对象的value属性,就是紧跟在return语句后面的表达式的值(如果没有return语句,则value属性的值为undefined),done属性的值true,表示遍历已经结束。
|
||||
第三次调用,Generator函数从上次yield语句停下的地方,一直执行到return语句(如果没有return语句,就执行到函数结束)。next方法返回的对象的value属性,就是紧跟在return语句后面的表达式的值(如果没有return语句,则value属性的值为undefined),done属性的值true,表示遍历已经结束。
|
||||
|
||||
第四次调用,此时函数已经运行完毕,next方法返回对象的value属性为undefined,done属性为true。以后再调用next方法,返回的都是这个值。
|
||||
第四次调用,此时Generator函数已经运行完毕,next方法返回对象的value属性为undefined,done属性为true。以后再调用next方法,返回的都是这个值。
|
||||
|
||||
总结一下,Generator函数使用iterator接口,每次调用next方法的返回值,就是一个标准的iterator返回值:有着value和done两个属性的对象。其中,value是yield语句后面那个表达式的值,done是一个布尔值,表示是否遍历结束。
|
||||
总结一下,调用Generator函数,返回一个部署了Iterator接口的遍历器对象,用来操作内部指针。以后,每次调用遍历器对象的next方法,就会返回一个有着value和done两个属性的对象。value属性表示当前的内部状态的值,是yield语句后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束。
|
||||
|
||||
上一章说过,任意一个对象的Symbol.iterator属性,等于该对象的遍历器函数,即调用该函数会返回该对象的一个遍历器。遍历器本身也是一个对象,它的Symbol.iterator属性执行后,返回自身。
|
||||
### yield语句
|
||||
|
||||
由于Generator函数返回的遍历器,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield语句就是暂停标志。
|
||||
|
||||
遍历器next方法的运行逻辑如下。
|
||||
|
||||
(1)遇到yield语句,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。
|
||||
|
||||
(2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield语句。
|
||||
|
||||
(3)如果没有再遇到新的yield语句,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。
|
||||
|
||||
(4)如果该函数没有return语句,则返回的对象的value属性值为undefined。
|
||||
|
||||
需要注意的是,yield语句后面的表达式,只有当调用next方法、内部指针指向该语句时才会执行,因此等于为JavaScript提供了手动的“惰性求值”(Lazy Evaluation)的语法功能。
|
||||
|
||||
```javascript
|
||||
|
||||
function* gen(){
|
||||
// some code
|
||||
function* gen{
|
||||
yield 123 + 456;
|
||||
}
|
||||
|
||||
var g = gen();
|
||||
|
||||
g[Symbol.iterator]() === g
|
||||
// true
|
||||
|
||||
```
|
||||
|
||||
上面代码中,gen是一个Generator函数,调用它会生成一个遍历器g。遍历器g的Symbol.iterator属性是一个遍历器函数,执行后返回它自己。
|
||||
上面代码中,yield后面的表达式`123 + 456`,不会立即求值,只会在next方法将指针移到这一句时,才会求值。
|
||||
|
||||
由于Generator函数返回的遍历器,只有调用next方法才会遍历下一个成员,所以其实提供了一种可以暂停执行的函数。yield语句就是暂停标志,next方法遇到yield,就会暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回对象的value属性的值。当下一次调用next方法时,再继续往下执行,直到遇到下一个yield语句。如果没有再遇到新的yield语句,就一直运行到函数结束,将return语句后面的表达式的值,作为value属性的值,如果该函数没有return语句,则value属性的值为undefined。另一方面,由于yield后面的表达式,直到调用next方法时才会执行,因此等于为JavaScript提供了手动的“惰性求值”(Lazy Evaluation)的语法功能。
|
||||
|
||||
yield语句与return语句有点像,都能返回紧跟在语句后面的那个表达式的值。区别在于每次遇到yield,函数暂停执行,下一次再从该位置继续向后执行,而return语句不具备位置记忆的功能。一个函数里面,只能执行一次(或者说一个)return语句,但是可以执行多次(或者说多个)yield语句。正常函数只能返回一个值,因为只能执行一次return;Generator函数可以返回一系列的值,因为可以有任意多个yield。从另一个角度看,也可以说Generator生成了一系列的值,这也就是它的名称的来历(在英语中,generator这个词是“生成器”的意思)。
|
||||
yield语句与return语句既有相似之处,也有区别。相似之处在于,都能返回紧跟在语句后面的那个表达式的值。区别在于每次遇到yield,函数暂停执行,下一次再从该位置继续向后执行,而return语句不具备位置记忆的功能。一个函数里面,只能执行一次(或者说一个)return语句,但是可以执行多次(或者说多个)yield语句。正常函数只能返回一个值,因为只能执行一次return;Generator函数可以返回一系列的值,因为可以有任意多个yield。从另一个角度看,也可以说Generator生成了一系列的值,这也就是它的名称的来历(在英语中,generator这个词是“生成器”的意思)。
|
||||
|
||||
Generator函数可以不用yield语句,这时就变成了一个单纯的暂缓执行函数。
|
||||
|
||||
```javascript
|
||||
|
||||
function* f() {
|
||||
console.log('执行了!')
|
||||
}
|
||||
@ -82,7 +90,6 @@ var generator = f();
|
||||
setTimeout(function () {
|
||||
generator.next()
|
||||
}, 2000);
|
||||
|
||||
```
|
||||
|
||||
上面代码中,函数f如果是普通函数,在为变量generator赋值时就会执行。但是,函数f是一个Generator函数,就变成只有调用next方法时,函数f才会执行。
|
||||
@ -90,12 +97,10 @@ setTimeout(function () {
|
||||
另外需要注意,yield语句不能用在普通函数中,否则会报错。
|
||||
|
||||
```javascript
|
||||
|
||||
(function (){
|
||||
yield 1;
|
||||
})()
|
||||
// SyntaxError: Unexpected number
|
||||
|
||||
```
|
||||
|
||||
上面代码在一个普通函数中使用yield语句,结果产生一个句法错误。
|
||||
@ -103,7 +108,6 @@ setTimeout(function () {
|
||||
下面是另外一个例子。
|
||||
|
||||
```javascript
|
||||
|
||||
var arr = [1, [[2, 3], 4], [5, 6]];
|
||||
|
||||
var flat = function* (a){
|
||||
@ -119,7 +123,6 @@ var flat = function* (a){
|
||||
for (var f of flat(arr)){
|
||||
console.log(f);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
上面代码也会产生句法错误,因为forEach方法的参数是一个普通函数,但是在里面使用了yield语句。一种修改方法是改用for循环。
|
||||
@ -145,6 +148,25 @@ for (var f of flat(arr)){
|
||||
// 1, 2, 3, 4, 5, 6
|
||||
```
|
||||
|
||||
### 与Iterator的关系
|
||||
|
||||
上一章说过,任意一个对象的Symbol.iterator属性,等于该对象的遍历器函数,调用该函数会返回该对象的一个遍历器。
|
||||
|
||||
遍历器本身也是一个对象,它的Symbol.iterator属性执行后,返回自身。
|
||||
|
||||
```javascript
|
||||
function* gen(){
|
||||
// some code
|
||||
}
|
||||
|
||||
var g = gen();
|
||||
|
||||
g[Symbol.iterator]() === g
|
||||
// true
|
||||
```
|
||||
|
||||
上面代码中,gen是一个Generator函数,调用它会生成一个遍历器g。遍历器g的Symbol.iterator属性是一个遍历器函数,执行后返回它自己。
|
||||
|
||||
## next方法的参数
|
||||
|
||||
yield语句本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield语句的返回值。
|
||||
|
@ -1,19 +1,28 @@
|
||||
# Iterator和for...of循环
|
||||
|
||||
## Iterator(遍历器)
|
||||
## Iterator(遍历器)的概念
|
||||
|
||||
### 概念
|
||||
JavaScript原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6又添加了Map和Set。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map,Map的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。
|
||||
|
||||
JavaScript原有的数据结构,主要是数组(Array)和对象(Object),ES6又添加了Map和Set,用户还可以组合使用它们,定义自己的数据结构。这就需要一种统一的接口机制,来处理所有不同的数据结构。
|
||||
遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
|
||||
|
||||
遍历器(Iterator)就是这样一种机制。它属于一种接口规格,任何数据结构只要部署这个接口,就可以完成遍历操作,即依次处理该结构的所有成员。它的作用有两个,一是为各种数据结构,提供一个统一的、简便的接口,二是使得数据结构的成员能够按某种次序排列。在ES6中,遍历操作特指for...of循环,即Iterator接口主要供for...of消费。
|
||||
Iterator的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。
|
||||
|
||||
遍历器的遍历过程是这样的:它提供了一个指针,默认指向当前数据结构的起始位置。也就是说,遍历器返回一个内部指针。第一次调用遍历器的next方法,可以将指针指向到第一个成员,第二次调用next方法,就指向第二个成员,直至指向数据结构的结束位置。每一次调用,都会返回当前成员的信息,具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
|
||||
Iterator的遍历过程是这样的。
|
||||
|
||||
(1)创建一个指针,指向当前数据结构的起始位置。也就是说,遍历器的返回值是一个指针对象。
|
||||
|
||||
(2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
|
||||
|
||||
(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
|
||||
|
||||
(4)调用指针对象的next方法,直到它指向数据结构的结束位置。
|
||||
|
||||
每一次调用next方法,都会返回当前成员的信息,具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
|
||||
|
||||
下面是一个模拟next方法返回值的例子。
|
||||
|
||||
```javascript
|
||||
|
||||
function makeIterator(array){
|
||||
var nextIndex = 0;
|
||||
return {
|
||||
@ -30,21 +39,19 @@ var it = makeIterator(['a', 'b']);
|
||||
it.next() // { value: "a", done: false }
|
||||
it.next() // { value: "b", done: false }
|
||||
it.next() // { value: undefined, done: true }
|
||||
|
||||
```
|
||||
|
||||
上面代码定义了一个makeIterator函数,它的作用就是返回数组的遍历器。对数组`['a', 'b']`执行这个函数,就会返回该数组的遍历器it。
|
||||
上面代码定义了一个makeIterator函数,它的作用就是返回数组的指针对象。对数组`['a', 'b']`执行这个函数,就会返回该数组的指针对象it。
|
||||
|
||||
遍历器it的next方法,用来移动指针。开始时,指针指向数组的开始位置。然后,每次调用next方法,指针就会指向数组的下一个成员。第一次调用,指向a;第二次调用,指向b。
|
||||
指针对象的next方法,用来移动指针。开始时,指针指向数组的开始位置。然后,每次调用next方法,指针就会指向数组的下一个成员。第一次调用,指向a;第二次调用,指向b。
|
||||
|
||||
next方法返回一个对象,表示当前位置的信息。这个对象具有value和done两个属性,value属性返回当前位置的成员,done属性是一个布尔值,表示遍历是否结束,即是否还有必要再一次调用next方法。
|
||||
next方法返回一个对象,表示当前数据成员的信息。这个对象具有value和done两个属性,value属性返回当前位置的成员,done属性是一个布尔值,表示遍历是否结束,即是否还有必要再一次调用next方法。
|
||||
|
||||
总之,遍历器是一个对象,具有next方法。调用next方法,就可以遍历事先给定的数据结构。
|
||||
总之,指针对象具有next方法。调用next方法,就可以遍历事先给定的数据结构。
|
||||
|
||||
由于遍历器只是把接口规格加到数据结构之上,所以,遍历器与它所遍历的那个数据结构,实际上是分开的,完全可以写出没有对应数据结构的遍历器,或者说用遍历器模拟出数据结构。下面是一个无限运行的遍历器例子。
|
||||
由于Iterator只是把接口规格加到数据结构之上,所以,遍历器与它所遍历的那个数据结构,实际上是分开的,完全可以写出没有对应数据结构的遍历器,或者说用遍历器模拟出数据结构。下面是一个无限运行的遍历器例子。
|
||||
|
||||
```javascript
|
||||
|
||||
function idMaker(){
|
||||
var index = 0;
|
||||
|
||||
@ -61,17 +68,15 @@ it.next().value // '0'
|
||||
it.next().value // '1'
|
||||
it.next().value // '2'
|
||||
// ...
|
||||
|
||||
```
|
||||
|
||||
上面的例子中,idMaker函数返回的对象就是遍历器,但是并没有对应的数据结构,或者说遍历器自己描述了一个数据结构出来。
|
||||
上面的例子中,遍历器idMaker函数返回的指针对象,并没有对应的数据结构,或者说遍历器自己描述了一个数据结构出来。
|
||||
|
||||
在ES6中,有些数据结构原生提供遍历器(比如数组),即不用任何处理,就可以被for...of循环遍历,有些就不行(比如对象)。原因在于,这些数据结构部署了System.iterator属性(详见下文)。凡是部署了System.iterator属性的数据结构,就称为部署了遍历器接口。调用这个接口,就会返回一个遍历器。
|
||||
在ES6中,有些数据结构原生提供遍历器(比如数组),即不用任何处理,就可以被for...of循环遍历,有些就不行(比如对象)。原因在于,这些数据结构原生部署了System.iterator属性(详见下文),有些没有。凡是部署了System.iterator属性的数据结构,就称为部署了遍历器接口。调用这个接口,就会返回一个指针对象。
|
||||
|
||||
如果使用TypeScript的写法,遍历器接口、遍历器和next方法返回值的规格可以描述如下。
|
||||
如果使用TypeScript的写法,遍历器接口(Iterable)、指针对象(Iterator)和next方法返回值的规格可以描述如下。
|
||||
|
||||
```javascript
|
||||
|
||||
interface Iterable {
|
||||
[System.iterator]() : Iterator,
|
||||
}
|
||||
@ -84,19 +89,17 @@ interface IterationResult {
|
||||
value: any,
|
||||
done: boolean,
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### 默认的Iterator接口
|
||||
## 数据结构的默认Iterator接口
|
||||
|
||||
Iterator接口的目的,就是为所有数据结构,提供了一种统一的访问机制,即for...of循环(详见下文)。当使用for...of循环遍历某种数据结构时,该循环会自动去寻找Iterator接口。
|
||||
|
||||
ES6规定,默认的Iterator接口部署在数据结构的Symbol.iterator属性,或者一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。也就是说,调用Symbol.iterator方法,就会得到当前数据结构的默认遍历器。Symbol.iterator本身是一个表达式,返回Symbol对象的iterator属性,这是一个预定义好的、类型为Symbol的特殊值,所以要放在方括号内(请参考Symbol一节)。
|
||||
ES6规定,默认的Iterator接口部署在数据结构的`Symbol.iterator`属性,或者一个数据结构只要具有`Symbol.iterator`属性,就可以认为是“可遍历的”(iterable)。也就是说,调用`Symbol.iterator`方法,就会得到当前数据结构的默认遍历器。`Symbol.iterator`本身是一个表达式,返回Symbol对象的iterator属性,这是一个预定义好的、类型为Symbol的特殊值,所以要放在方括号内(请参考Symbol一节)。
|
||||
|
||||
在ES6中,有三类数据结构原生具备Iterator接口:数组、某些类似数组的对象、Set和Map结构。
|
||||
|
||||
```javascript
|
||||
|
||||
let arr = ['a', 'b', 'c'];
|
||||
let iter = arr[Symbol.iterator]();
|
||||
|
||||
@ -104,7 +107,6 @@ iter.next() // { value: 'a', done: false }
|
||||
iter.next() // { value: 'b', done: false }
|
||||
iter.next() // { value: 'c', done: false }
|
||||
iter.next() // { value: undefined, done: true }
|
||||
|
||||
```
|
||||
|
||||
上面代码中,变量arr是一个数组,原生就具有遍历器接口,部署在arr的Symbol.iterator属性上面。所以,调用这个属性,就得到遍历器。
|
||||
@ -116,7 +118,6 @@ iter.next() // { value: undefined, done: true }
|
||||
一个对象如果要有可被for...of循环调用的Iterator接口,就必须在Symbol.iterator的属性上部署遍历器方法(原型链上的对象具有该方法也可)。
|
||||
|
||||
```javascript
|
||||
|
||||
class RangeIterator {
|
||||
constructor(start, stop) {
|
||||
this.value = start;
|
||||
@ -143,7 +144,6 @@ function range(start, stop) {
|
||||
for (var value of range(0, 3)) {
|
||||
console.log(value);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
上面代码是一个类部署Iterator接口的写法。Symbol.iterator属性对应一个函数,执行后返回当前对象的遍历器。
|
||||
@ -151,7 +151,6 @@ for (var value of range(0, 3)) {
|
||||
下面是通过遍历器实现指针结构的例子。
|
||||
|
||||
```javascript
|
||||
|
||||
function Obj(value){
|
||||
this.value = value;
|
||||
this.next = null;
|
||||
@ -196,7 +195,6 @@ for (var i of one){
|
||||
// 1
|
||||
// 2
|
||||
// 3
|
||||
|
||||
```
|
||||
|
||||
上面代码首先在构造函数的原型链上部署Symbol.iterator方法,调用该方法会返回遍历器对象iterator,调用该对象的next方法,在返回一个值的同时,自动将内部指针移到下一个实例。
|
||||
@ -204,7 +202,6 @@ for (var i of one){
|
||||
下面是另一个为对象添加Iterator接口的例子。
|
||||
|
||||
```javascript
|
||||
|
||||
let obj = {
|
||||
data: [ 'hello', 'world' ],
|
||||
[Symbol.iterator]() {
|
||||
@ -224,27 +221,22 @@ let obj = {
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
对于类似数组的对象(存在数值键名和length属性),部署Iterator接口,有一个简便方法,就是`Symbol.iterator`方法直接引用数值的Iterator接口。
|
||||
|
||||
```javascript
|
||||
|
||||
NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
|
||||
|
||||
```
|
||||
|
||||
如果Symbol.iterator方法返回的不是遍历器,解释引擎将会报错。
|
||||
|
||||
```javascript
|
||||
|
||||
var obj = {};
|
||||
|
||||
obj[Symbol.iterator] = () => 1;
|
||||
|
||||
[...obj] // TypeError: [] is not a function
|
||||
|
||||
```
|
||||
|
||||
上面代码中,变量obj的Symbol.iterator方法返回的不是遍历器,因此报错。
|
||||
@ -263,7 +255,7 @@ while (!$result.done) {
|
||||
|
||||
上面代码中,ITERABLE代表某种可遍历的数据结构,$iterator是它的遍历器。遍历器每次移动指针(next方法),都检查一下返回值的done属性,如果遍历还没结束,就移动遍历器的指针到下一步(next方法),不断循环。
|
||||
|
||||
### 调用默认iterator接口的场合
|
||||
## 调用默认Iterator接口的场合
|
||||
|
||||
有一些场合会默认调用iterator接口(即Symbol.iterator方法),除了下文会介绍的for...of循环,还有几个别的场合。
|
||||
|
||||
@ -288,7 +280,6 @@ let [first, ...rest] = set;
|
||||
扩展运算符(...)也会调用默认的iterator接口。
|
||||
|
||||
```javascript
|
||||
|
||||
// 例一
|
||||
var str = 'hello';
|
||||
[...str] // ['h','e','l','l','o']
|
||||
@ -297,7 +288,6 @@ var str = 'hello';
|
||||
let arr = ['b', 'c'];
|
||||
['a', ...arr, 'd']
|
||||
// ['a', 'b', 'c', 'd']
|
||||
|
||||
```
|
||||
|
||||
上面代码的扩展运算符内部就调用iterator接口。
|
||||
@ -305,9 +295,7 @@ let arr = ['b', 'c'];
|
||||
实际上,这提供了一种简便机制,可以将任何部署了iterator接口的数据结构,转为数组。也就是说,只要某个数据结构部署了iterator接口,就可以对它使用扩展运算符,将其转为数组。
|
||||
|
||||
```javascript
|
||||
|
||||
let arr = [...iterable];
|
||||
|
||||
```
|
||||
|
||||
**(3)其他场合**
|
||||
@ -319,7 +307,7 @@ let arr = [...iterable];
|
||||
- Map(), Set(), WeakMap(), WeakSet()
|
||||
- Promise.all(), Promise.race()
|
||||
|
||||
### 原生具备iterator接口的数据结构
|
||||
## 原生具备Iterator接口的数据结构
|
||||
|
||||
《数组的扩展》一章中提到,ES6对数组提供entries()、keys()和values()三个方法,就是返回三个遍历器。
|
||||
|
||||
@ -359,7 +347,6 @@ iterator.next() // { value: undefined, done: true }
|
||||
可以覆盖原生的`Symbol.iterator`方法,达到修改遍历器行为的目的。
|
||||
|
||||
```javascript
|
||||
|
||||
var str = new String("hi");
|
||||
|
||||
[...str] // ["h", "i"]
|
||||
@ -380,17 +367,15 @@ str[Symbol.iterator] = function() {
|
||||
|
||||
[...str] // ["bye"]
|
||||
str // "hi"
|
||||
|
||||
```
|
||||
|
||||
上面代码中,字符串str的`Symbol.iterator`方法被修改了,所以扩展运算符(...)返回的值变成了bye,而字符串本身还是hi。
|
||||
|
||||
### Iterator接口与Generator函数
|
||||
## Iterator接口与Generator函数
|
||||
|
||||
部署`Symbol.iterator`方法的最简单实现,还是使用下一节要提到的Generator函数。
|
||||
`Symbol.iterator`方法的最简单实现,还是使用下一章要介绍的Generator函数。
|
||||
|
||||
```javascript
|
||||
|
||||
var myIterable = {};
|
||||
|
||||
myIterable[Symbol.iterator] = function* () {
|
||||
@ -414,16 +399,17 @@ for (let x of obj) {
|
||||
}
|
||||
// hello
|
||||
// world
|
||||
|
||||
```
|
||||
|
||||
### return(),throw()
|
||||
上面代码中,`Symbol.iterator`方法几乎不用部署任何代码,只要用yield命令给出每一步的返回值即可。
|
||||
|
||||
遍历器除了具有next方法(必备),还可以具有return方法和throw方法(可选)。
|
||||
## 遍历器的return(),throw()
|
||||
|
||||
for...of循环如果提前退出(通常是因为出错,或者有break语句或continue语句),就会调用return方法。如果一个对象在完成遍历前,需要清理或释放资源,就可以部署return方法。
|
||||
遍历器返回的指针对象除了具有next方法,还可以具有return方法和throw方法。其中,next方法是必须部署的,return方法和throw方法是否部署是可选的。
|
||||
|
||||
throw方法主要是配合Generator函数使用,一般的遍历器用不到这个方法。请参阅《Generator函数》的章节。
|
||||
return方法的使用场合是,如果for...of循环提前退出(通常是因为出错,或者有break语句或continue语句),就会调用return方法。如果一个对象在完成遍历前,需要清理或释放资源,就可以部署return方法。
|
||||
|
||||
throw方法主要是配合Generator函数使用,一般的遍历器用不到这个方法。请参阅《Generator函数》一章。
|
||||
|
||||
## for...of循环
|
||||
|
||||
|
@ -2,14 +2,15 @@
|
||||
|
||||
## 基本用法
|
||||
|
||||
ES6原生提供了Promise对象。所谓Promise对象,就是代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的API,可供进一步处理。
|
||||
Promise在JavaScript语言早有实现,ES6将其写进了语言标准,统一了用法,原生提供了Promise对象。
|
||||
|
||||
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供的接口,使得控制异步操作更加容易。Promise对象的概念的详细解释,请参考[《JavaScript标准参考教程》](http://javascript.ruanyifeng.com/)。
|
||||
所谓Promise,就是一个对象,用来传递异步操作的消息。它代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的API,可供进一步处理。
|
||||
|
||||
ES6的Promise对象是一个构造函数,用来生成Promise实例。
|
||||
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供的接口,使得控制异步操作更加容易。
|
||||
|
||||
ES6规定,Promise对象是一个构造函数,用来生成Promise实例。
|
||||
|
||||
```javascript
|
||||
|
||||
var promise = new Promise(function(resolve, reject) {
|
||||
if (/* 异步操作成功 */){
|
||||
resolve(value);
|
||||
@ -23,7 +24,6 @@ promise.then(function(value) {
|
||||
}, function(value) {
|
||||
// failure
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
上面代码中,Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve方法和reject方法。如果异步操作成功,则用resolve方法将Promise对象的状态,从“未完成”变为“成功”(即从pending变为resolved);如果异步操作失败,则用reject方法将Promise对象的状态,从“未完成”变为“失败”(即从pending变为rejected)。
|
||||
|
Loading…
x
Reference in New Issue
Block a user