1
0
mirror of https://github.com/ruanyf/es6tutorial.git synced 2025-05-24 18:32:22 +00:00

修改iterator

This commit is contained in:
Ruan Yifeng 2014-12-28 22:01:28 +08:00
parent 78cacd8b27
commit 189741eeed
5 changed files with 258 additions and 61 deletions

View File

@ -99,6 +99,17 @@ const [v1, v2, ..., vN ] = array;
```
对于Set结构也可以使用数组的解构赋值。
```javascript
[a, b, c] = new Set(["a", "b", "c"])
a // "a"
```
事实上只要某种数据结构具有Iterator接口都可以采用数组形式的结构赋值。
## 对象的解构赋值
解构不仅可以用于数组,还可以用于对象。

View File

@ -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允许使用“箭头”=>)定义函数。

View File

@ -274,6 +274,47 @@ for(let value of delegatingIterator) {
上面代码中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函数之中,然后用一个任务管理函数执行(参考本节的“控制流管理”部分)

View File

@ -2,17 +2,14 @@
## 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 ?
@ -24,12 +21,12 @@ function makeIterator(array){
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返回值的构造。
下面是一个无限运行的遍历器例子。
@ -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对象的例子

View File

@ -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)
## 工具