mirror of
https://github.com/ruanyf/es6tutorial.git
synced 2025-05-24 10:22:23 +00:00
修改iterator
This commit is contained in:
parent
78cacd8b27
commit
189741eeed
@ -99,6 +99,17 @@ const [v1, v2, ..., vN ] = array;
|
||||
|
||||
```
|
||||
|
||||
对于Set结构,也可以使用数组的解构赋值。
|
||||
|
||||
```javascript
|
||||
|
||||
[a, b, c] = new Set(["a", "b", "c"])
|
||||
a // "a"
|
||||
|
||||
```
|
||||
|
||||
事实上,只要某种数据结构具有Iterator接口,都可以采用数组形式的结构赋值。
|
||||
|
||||
## 对象的解构赋值
|
||||
|
||||
解构不仅可以用于数组,还可以用于对象。
|
||||
|
@ -219,6 +219,31 @@ d
|
||||
|
||||
```
|
||||
|
||||
扩展运算符还可以将字符串转为真正的数组。
|
||||
|
||||
```javascript
|
||||
|
||||
[..."hello"]
|
||||
// [ "h", "e", "l", "l", "o" ]
|
||||
|
||||
```
|
||||
|
||||
扩展运算符内部调用的是数据结构的Iterator接口,因此只要具有Iterator接口的对象,都可以使用扩展运算符。
|
||||
|
||||
```javascript
|
||||
|
||||
var go = function*(){
|
||||
yield 1;
|
||||
yield 2;
|
||||
yield 3;
|
||||
};
|
||||
|
||||
[...go()] // [1, 2, 3]
|
||||
|
||||
```
|
||||
|
||||
上面代码中,变量go是一个Generator函数,执行后返回的是一个遍历器,对这个遍历器执行扩展运算符,就会将内部遍历得到的值,转为一个数组。
|
||||
|
||||
## 箭头函数
|
||||
|
||||
ES6允许使用“箭头”(=>)定义函数。
|
||||
|
@ -268,12 +268,53 @@ for(let value of delegatingIterator) {
|
||||
// "Greetings!
|
||||
// "Hello!"
|
||||
// "Bye!"
|
||||
// "Ok, bye."
|
||||
// "Ok, bye."
|
||||
|
||||
```
|
||||
|
||||
上面代码中,delegatingIterator是代理者,delegatedIterator是被代理者。由于`yield* delegatedIterator`语句得到的值,是一个遍历器,所以要用星号表示。运行结果就是使用一个遍历器,遍历了多个Genertor函数,有递归的效果。
|
||||
|
||||
如果`yield*`后面跟着一个数组,就表示该数组会返回一个遍历器,因此就会遍历数组成员。
|
||||
|
||||
```javascript
|
||||
|
||||
function* gen(){
|
||||
yield* ["a", "b", "c"];
|
||||
}
|
||||
|
||||
gen().next() // { value:"a", done:false }
|
||||
|
||||
```
|
||||
|
||||
上面代码中,yield命令后面如果不加星号,返回的是整个数组,加了星号就表示返回的是数组的遍历器。
|
||||
|
||||
`yield*`命令可以很方便地取出嵌套数组的所有成员。
|
||||
|
||||
```javascript
|
||||
|
||||
function* iterTree(tree) {
|
||||
if (Array.isArray(tree)) {
|
||||
for(let i=0; i < tree.length; i++) {
|
||||
yield* iterTree(tree[i]);
|
||||
}
|
||||
} else {
|
||||
yield tree;
|
||||
}
|
||||
}
|
||||
|
||||
const tree = [ 'a', ['b', 'c'], ['d', 'e'] ];
|
||||
|
||||
for(let x of iterTree(tree)) {
|
||||
console.log(x);
|
||||
}
|
||||
// a
|
||||
// b
|
||||
// c
|
||||
// d
|
||||
// e
|
||||
|
||||
```
|
||||
|
||||
下面是一个稍微复杂的例子,使用yield*语句遍历完全二叉树。
|
||||
|
||||
```javascript
|
||||
@ -522,6 +563,26 @@ for (let [key, value] of iterEntries(myObj)) {
|
||||
|
||||
上述代码中,myObj是一个普通对象,通过iterEntries函数,就有了iterator接口。也就是说,可以在任意对象上部署next方法。
|
||||
|
||||
下面是一个对数组部署Iterator接口的例子,尽管数组原生具有这个接口。
|
||||
|
||||
```javascript
|
||||
|
||||
function* makeSimpleGenerator(array){
|
||||
var nextIndex = 0;
|
||||
|
||||
while(nextIndex < array.length){
|
||||
yield array[nextIndex++];
|
||||
}
|
||||
}
|
||||
|
||||
var gen = makeSimpleGenerator(['yo', 'ya']);
|
||||
|
||||
gen.next().value // 'yo'
|
||||
gen.next().value // 'ya'
|
||||
gen.next().done // true
|
||||
|
||||
```
|
||||
|
||||
### (4)作为数据结构
|
||||
|
||||
Generator可以看作是数据结构,更确切地说,可以看作是一个数组结构,因为Generator函数可以返回一系列的值,这意味着它可以对任意表达式,提供类似数组的接口。
|
||||
@ -570,4 +631,4 @@ function doStuff() {
|
||||
|
||||
Generator函数是ECMAScript 6对协程的实现,但属于不完全实现,只做到了暂停执行和转移执行权,有一些特性没有实现,比如不支持所调用的函数之中的yield语句。
|
||||
|
||||
如果将Generator函数看作多任务运行的方式,存在多个进入点和退出点。那么,一方面,并发的多任务可以写成多个Generator函数;另一方面,继发的任务则可以按照发生顺序,写在一个Generator函数之中。
|
||||
如果将Generator函数看作多任务运行的方式,存在多个进入点和退出点。那么,一方面,并发的多任务可以写成多个Generator函数;另一方面,继发的任务则可以按照发生顺序,写在一个Generator函数之中,然后用一个任务管理函数执行(参考本节的“控制流管理”部分)。
|
||||
|
214
docs/iterator.md
214
docs/iterator.md
@ -2,47 +2,44 @@
|
||||
|
||||
## Iterator(遍历器)
|
||||
|
||||
遍历器(Iterator)是一种协议,任何对象只要部署这个协议,就可以完成遍历操作。在ES6中,遍历操作特指for...of循环。
|
||||
遍历器(Iterator)是一种接口规格,任何对象只要部署这个接口,就可以完成遍历操作。它的作用有两个,一是为各种数据结构,提供一个统一的、简便的接口,二是使得对象的属性能够按某种次序排列。在ES6中,遍历操作特指for...of循环,即Iterator接口主要供for...of循环使用。
|
||||
|
||||
它的作用主要有两个,一是为遍历对象的属性提供统一的接口,二是为使得对象的属性能够按次序排列。
|
||||
|
||||
ES6的遍历器协议规定,部署了next方法的对象,就具备了遍历器功能。next方法必须返回一个包含value和done两个属性的对象。其中,value属性是当前遍历位置的值,done属性是一个布尔值,表示遍历是否结束。
|
||||
遍历器提供了一个指针,指向当前对象的某个属性,使用next方法,就可以将指针移动到下一个属性。next方法返回一个包含value和done两个属性的对象。其中,value属性是当前遍历位置的值,done属性是一个布尔值,表示遍历是否结束。下面是一个模拟next方法返回值的例子。
|
||||
|
||||
```javascript
|
||||
|
||||
function makeIterator(array){
|
||||
var nextIndex = 0;
|
||||
|
||||
return {
|
||||
next: function(){
|
||||
return nextIndex < array.length ?
|
||||
{value: array[nextIndex++], done: false} :
|
||||
{value: undefined, done: true};
|
||||
}
|
||||
var nextIndex = 0;
|
||||
return {
|
||||
next: function(){
|
||||
return nextIndex < array.length ?
|
||||
{value: array[nextIndex++], done: false} :
|
||||
{value: undefined, done: true};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var it = makeIterator(['a', 'b']);
|
||||
|
||||
it.next().value // 'a'
|
||||
it.next().value // 'b'
|
||||
it.next().done // true
|
||||
it.next() // { value: "a", done: false }
|
||||
it.next() // { value: "b", done: false }
|
||||
it.next() // { value: undefined, done: true }
|
||||
|
||||
```
|
||||
上面代码定义了一个makeIterator函数,它的作用是返回一个遍历器对象,用来遍历参数数组。请特别注意,next返回值的构造。
|
||||
上面代码定义了一个makeIterator函数,它的作用是返回一个遍历器对象,用来遍历参数数组。next方法依次遍历数组的每个成员,请特别注意,next返回值的构造。
|
||||
|
||||
下面是一个无限运行的遍历器例子。
|
||||
|
||||
```javascript
|
||||
|
||||
function idMaker(){
|
||||
var index = 0;
|
||||
var index = 0;
|
||||
|
||||
return {
|
||||
next: function(){
|
||||
return {value: index++, done: false};
|
||||
}
|
||||
return {
|
||||
next: function(){
|
||||
return {value: index++, done: false};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var it = idMaker();
|
||||
@ -54,29 +51,135 @@ it.next().value // '2'
|
||||
|
||||
```
|
||||
|
||||
在ES6中,数组、类似数组的对象、Set和Map结构,都原生具备iterator接口,可以被for...of循环遍历。
|
||||
上面的例子,只是为了说明next方法返回值的结构。Iterator接口返回的遍历器,原生具备next方法,不用自己部署。所以,真正需要部署的是Iterator接口,让其返回一个遍历器。在ES6中,有三类数据结构原生具备Iterator接口:数组、类似数组的对象、Set和Map结构。除此之外,其他数据结构(主要是对象)的Iterator接口都需要自己部署。
|
||||
|
||||
## for...of循环
|
||||
|
||||
ES6中,一个对象只要部署了next方法,就被视为具有iterator接口,就可以用for...of循环遍历它的值。下面用上一节的idMaker函数生成的it遍历器作为例子。
|
||||
下面就是如何部署Iterator接口。一个对象如果要有Iterator接口,必须部署一个@@iterator方法(原型链上的对象具有该方法也可),该方法部署在一个键名为`Symbol.iterator`的属性上,对应的键值是一个函数,该函数返回一个遍历器对象。
|
||||
|
||||
```javascript
|
||||
|
||||
for (var n of it) {
|
||||
if (n > 5)
|
||||
break;
|
||||
console.log(n);
|
||||
class MySpecialTree {
|
||||
// ...
|
||||
[Symbol.iterator]() {
|
||||
// ...
|
||||
return theIterator;
|
||||
}
|
||||
}
|
||||
// 0
|
||||
// 1
|
||||
// 2
|
||||
// 3
|
||||
// 4
|
||||
// 5
|
||||
|
||||
```
|
||||
|
||||
上面代码说明,for...of默认从0开始循环。
|
||||
上面代码是一个类部署Iterator接口的写法。`Symbol.iterator`是一个表达式,返回一个Symbol对象的iterator属性,这是一个类型为Symbol的特殊值,所以要放在方括号内。这里要注意,@@iterator的键名是`Symbol.iterator`,该方法执行后,返回一个当前对象的遍历器。
|
||||
|
||||
《数组的扩展》一章中提到,ES6对数组提供entries()、keys()和values()三个方法,就是返回三个遍历器。
|
||||
|
||||
```javascript
|
||||
|
||||
var arr = [1, 5, 7];
|
||||
var arrEntries = arr.entries();
|
||||
|
||||
arrEntries.toString()
|
||||
// "[object Array Iterator]"
|
||||
|
||||
arrEntries === arrEntries[Symbol.iterator]()
|
||||
// true
|
||||
|
||||
```
|
||||
|
||||
上面代码中,entries方法返回的是一个遍历器(iterator),本质上就是调用了`Symbol.iterator`方法。
|
||||
|
||||
字符串是一个类似数组的对象,因此也原生具有Iterator接口。
|
||||
|
||||
```javascript
|
||||
|
||||
var someString = "hi";
|
||||
typeof someString[Symbol.iterator]
|
||||
// "function"
|
||||
|
||||
var iterator = someString[Symbol.iterator]();
|
||||
|
||||
iterator.next() // { value: "h", done: false }
|
||||
iterator.next() // { value: "i", done: false }
|
||||
iterator.next() // { value: undefined, done: true }
|
||||
|
||||
```
|
||||
|
||||
上面代码中,调用`Symbol.iterator`方法返回一个遍历器,在这个遍历器上可以调用next方法,实现对于字符串的遍历。
|
||||
|
||||
可以覆盖原生的`Symbol.iterator`方法,达到修改遍历器行为的目的。
|
||||
|
||||
```javascript
|
||||
|
||||
var str = new String("hi");
|
||||
|
||||
[...str] // ["h", "i"]
|
||||
|
||||
str[Symbol.iterator] = function() {
|
||||
return {
|
||||
next: function() {
|
||||
if (this._first) {
|
||||
this._first = false;
|
||||
return { value: "bye", done: false };
|
||||
} else {
|
||||
return { done: true };
|
||||
}
|
||||
},
|
||||
_first: true
|
||||
};
|
||||
};
|
||||
|
||||
[...str] // ["bye"]
|
||||
str // "hi"
|
||||
|
||||
```
|
||||
|
||||
上面代码中,字符串str的`Symbol.iterator`方法被修改了,所以扩展运算符(...)返回的值变成了bye,而字符串本身还是hi。
|
||||
|
||||
部署`Symbol.iterator`方法的最简单实现,还是使用下一节要提到的Generator函数。
|
||||
|
||||
```javascript
|
||||
|
||||
var myIterable = {};
|
||||
|
||||
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
|
||||
|
||||
```
|
||||
|
||||
如果`Symbol.iterator`方法返回的不是遍历器,解释引擎将会报错。
|
||||
|
||||
```javascript
|
||||
|
||||
var obj = {};
|
||||
|
||||
obj[Symbol.iterator] = () => 1;
|
||||
|
||||
[...obj] // TypeError: [] is not a function
|
||||
|
||||
```
|
||||
|
||||
上面代码中,变量obj的@@iterator方法返回的不是遍历器,因此报错。
|
||||
|
||||
## for...of循环
|
||||
|
||||
ES6中,一个对象只要部署了@@iterator方法,就被视为具有iterator接口,就可以用for...of循环遍历它的值。也就是说,for...of循环内部调用是原对象的`Symbol.iterator`方法。
|
||||
|
||||
数组原生具备iterator接口。
|
||||
|
||||
@ -85,15 +188,23 @@ for (var n of it) {
|
||||
const arr = ['red', 'green', 'blue'];
|
||||
|
||||
for(let v of arr) {
|
||||
console.log(v);
|
||||
console.log(v); // red green blue
|
||||
}
|
||||
// red
|
||||
// green
|
||||
// blue
|
||||
|
||||
```
|
||||
|
||||
for...of循环完全可以取代数组实例的forEach方法。
|
||||
上面代码说明,for...of循环可以代替数组实例的forEach方法。
|
||||
|
||||
```javascript
|
||||
|
||||
const arr = ['red', 'green', 'blue'];
|
||||
|
||||
arr.forEach(function (element, index) {
|
||||
console.log(element); // red green blue
|
||||
console.log(index); // 0 1 2
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
JavaScript原有的for...in循环,只能获得对象的键名,不能直接获取键值。ES6提供for...of循环,允许遍历获得键值。
|
||||
|
||||
@ -101,26 +212,18 @@ JavaScript原有的for...in循环,只能获得对象的键名,不能直接
|
||||
|
||||
var arr = ["a", "b", "c", "d"];
|
||||
for (a in arr) {
|
||||
console.log(a);
|
||||
console.log(a); // 0 1 2 3
|
||||
}
|
||||
// 0
|
||||
// 1
|
||||
// 2
|
||||
// 3
|
||||
|
||||
for (a of arr) {
|
||||
console.log(a);
|
||||
console.log(a); // a b c d
|
||||
}
|
||||
// a
|
||||
// b
|
||||
// c
|
||||
// d
|
||||
|
||||
```
|
||||
|
||||
上面代码表明,for...in循环读取键名,for...of循环读取键值。如果要通过for...of循环,获取数组的索引,可以借助数组实例的entries方法和keys方法,参见《数组的扩展》章节。
|
||||
|
||||
对于Set和Map结构的数据,可以直接使用for...of循环。
|
||||
Set和Map结构也原生具有Iterator接口,可以直接使用for...of循环。
|
||||
|
||||
```javascript
|
||||
|
||||
@ -182,13 +285,8 @@ for (e of es6) {
|
||||
let str = "hello";
|
||||
|
||||
for (let s of str) {
|
||||
console.log(s);
|
||||
console.log(s); // h e l l o
|
||||
}
|
||||
// h
|
||||
// e
|
||||
// l
|
||||
// l
|
||||
// o
|
||||
|
||||
// DOM NodeList对象的例子
|
||||
|
||||
|
@ -35,9 +35,10 @@
|
||||
- Mozilla Developer Network, [WeakSet](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet):介绍WeakSet数据结构
|
||||
- Mathias Bynens, [Unicode-aware regular expressions in ES6](https://mathiasbynens.be/notes/es6-unicode-regex): 详细介绍正则表达式的u修饰符
|
||||
|
||||
## Generator
|
||||
## Iterator和Generator
|
||||
|
||||
- Mozilla Developer Network, [Iterators and generators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators)
|
||||
- Mozilla Developer Network, [The Iterator protocol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/The_Iterator_protocol)
|
||||
- Matt Baker, [Replacing callbacks with ES6 Generators](http://flippinawesome.org/2014/02/10/replacing-callbacks-with-es6-generators/)
|
||||
- Steven Sanderson, [Experiments with Koa and JavaScript Generators](http://blog.stevensanderson.com/2013/12/21/experiments-with-koa-and-javascript-generators/)
|
||||
- jmar777, [What's the Big Deal with Generators?](http://devsmash.com/blog/whats-the-big-deal-with-generators)
|
||||
@ -63,6 +64,7 @@
|
||||
- Jack Franklin, [JavaScript Modules the ES6 Way](http://24ways.org/2014/javascript-modules-the-es6-way/): ES6模块入门
|
||||
- Axel Rauschmayer, [ECMAScript 6 modules: the final syntax](http://www.2ality.com/2014/09/es6-modules-final.html): ES6模块的介绍,以及与CommonJS规格的详细比较
|
||||
- Dave Herman, [Static module resolution](http://calculist.org/blog/2012/06/29/static-module-resolution/): ES6模块的静态化设计思想
|
||||
- Axel Rauschmayer, [ECMAScript 6: new OOP features besides classes](http://www.2ality.com/2014/12/es6-oop.html)
|
||||
|
||||
## 工具
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user