# JavaScript 权威指南第七版（GPT 重译）（五）(1)

## 第十二章：迭代器和生成器

let sum = 0;
for(let i of [1,2,3]) { // Loop once for each of these values
sum += i;
}
sum   // => 6

let chars = [..."abcd"]; // chars == ["a", "b", "c", "d"]
let data = [1, 2, 3, 4, 5];
Math.max(...data)        // => 5

let purpleHaze = Uint8Array.of(255, 0, 255, 128);
let [r, g, b, a] = purpleHaze; // a == 128

let m = new Map([["one", 1], ["two", 2]]);
for(let [k,v] of m) console.log(k, v); // Logs 'one 1' and 'two 2'

[...m]            // => [["one", 1], ["two", 2]]: default iteration
[...m.entries()]  // => [["one", 1], ["two", 2]]: entries() method is the same
[...m.keys()]     // => ["one", "two"]: keys() method iterates just map keys
[...m.values()]   // => [1, 2]: values() method iterates just map values

// Strings are iterable, so the two sets are the same:
new Set("abc") // => new Set(["a", "b", "c"])

## 12.1 迭代器的工作原理

for/of循环和展开运算符与可迭代对象无缝配合，但值得理解实际上是如何使迭代工作的。在理解 JavaScript 中的迭代过程时，有三种不同的类型需要理解。首先是可迭代对象：这些是可以被迭代的类型，如 Array、Set 和 Map。其次，是执行迭代的迭代器对象本身。第三，是保存迭代每一步结果的迭代结果对象。

let iterable = [99];
let iterator = iterable[Symbol.iterator]();
for(let result = iterator.next(); !result.done; result = iterator.next()) {
console.log(result.value)  // result.value == 99
}

let list = [1,2,3,4,5];
let iter = list[Symbol.iterator]();
let tail = [...iter];          // tail == [2,3,4,5]

## 12.2 实现可迭代对象

###### 示例 12-1. 一个可迭代的数字范围类
/*
* A Range object represents a range of numbers {x: from <= x <= to}
* Range defines a has() method for testing whether a given number is a member
* of the range. Range is iterable and iterates all integers within the range.
*/
class Range {
constructor (from, to) {
this.from = from;
this.to = to;
}
// Make a Range act like a Set of numbers
has(x) { return typeof x === "number" && this.from <= x && x <= this.to; }
// Return string representation of the range using set notation
toString() { return { x | ${this.from} ≤ x ≤${this.to} }; }
// Make a Range iterable by returning an iterator object.
// Note that the name of this method is a special symbol, not a string.
[Symbol.iterator]() {
// Each iterator instance must iterate the range independently of
// others. So we need a state variable to track our location in the
// iteration. We start at the first integer >= from.
let next = Math.ceil(this.from);  // This is the next value we return
let last = this.to;               // We won't return anything > this
return {                          // This is the iterator object
// This next() method is what makes this an iterator object.
// It must return an iterator result object.
next() {
return (next <= last)   // If we haven't returned last value yet
? { value: next++ } // return next value and increment it
: { done: true };   // otherwise indicate that we're done.
},
// As a convenience, we make the iterator itself iterable.
[Symbol.iterator]() { return this; }
};
}
}
for(let x of new Range(1,10)) console.log(x); // Logs numbers 1 to 10
[...new Range(-2,2)]                          // => [-2, -1, 0, 1, 2]

// Return an iterable object that iterates the result of applying f()
// to each value from the source iterable
function map(iterable, f) {
let iterator = iterable[Symbol.iterator]();
return {     // This object is both iterator and iterable
[Symbol.iterator]() { return this; },
next() {
let v = iterator.next();
if (v.done) {
return v;
} else {
return { value: f(v.value) };
}
}
};
}
// Map a range of integers to their squares and convert to an array
[...map(new Range(1,4), x => x*x)]  // => [1, 4, 9, 16]
// Return an iterable object that filters the specified iterable,
// iterating only those elements for which the predicate returns true
function filter(iterable, predicate) {
let iterator = iterable[Symbol.iterator]();
return { // This object is both iterator and iterable
[Symbol.iterator]() { return this; },
next() {
for(;;) {
let v = iterator.next();
if (v.done || predicate(v.value)) {
return v;
}
}
}
};
}
// Filter a range so we're left with only even numbers
[...filter(new Range(1,10), x => x % 2 === 0)]  // => [2,4,6,8,10]

function words(s) {
var r = /\s+|\$/g;                     // Match one or more spaces or end
r.lastIndex = s.match(/[^ ]/).index;  // Start matching at first nonspace
return {                              // Return an iterable iterator object
[Symbol.iterator]() {             // This makes us iterable
return this;
},
next() {                          // This makes us an iterator
let start = r.lastIndex;      // Resume where the last match ended
if (start < s.length) {       // If we're not done
let match = r.exec(s);    // Match the next word boundary
if (match) {              // If we found one, return the word
return { value: s.substring(start, match.index) };
}
}
return { done: true };        // Otherwise, say that we're done
}
};
}
[...words(" abc def  ghi! ")] // => ["abc", "def", "ghi!"]

### 12.2.1 “关闭”迭代器：返回方法

for/of循环和展开运算符是 JavaScript 的非常有用的特性，因此在创建 API 时，尽可能使用它们是一个好主意。但是，必须使用可迭代对象、其迭代器对象和迭代器的结果对象来处理过程有些复杂。幸运的是，生成器可以极大地简化自定义迭代器的创建，我们将在本章的其余部分中看到。

## 12.3 生成器

// A generator function that yields the set of one digit (base-10) primes.
function* oneDigitPrimes() { // Invoking this function does not run the code
yield 2;                 // but just returns a generator object. Calling
yield 3;                 // the next() method of that generator runs
yield 5;                 // the code until a yield statement provides
yield 7;                 // the return value for the next() method.
}
// When we invoke the generator function, we get a generator
let primes = oneDigitPrimes();
// A generator is an iterator object that iterates the yielded values
primes.next().value          // => 2
primes.next().value          // => 3
primes.next().value          // => 5
primes.next().value          // => 7
primes.next().done           // => true
// Generators have a Symbol.iterator method to make them iterable
primes[Symbol.iterator]()    // => primes
// We can use generators like other iterable types
[...oneDigitPrimes()]        // => [2,3,5,7]
let sum = 0;
for(let prime of oneDigitPrimes()) sum += prime;
sum                          // => 17

const seq = function*(from,to) {
for(let i = from; i <= to; i++) yield i;
};
[...seq(3,5)]  // => [3, 4, 5]

let o = {
x: 1, y: 2, z: 3,
// A generator that yields each of the keys of this object
*g() {
for(let key of Object.keys(this)) {
yield key;
}
}
};
[...o.g()] // => ["x", "y", "z", "g"]

*[Symbol.iterator]() {
for(let x = Math.ceil(this.from); x <= this.to; x++) yield x;
}

### 12.3.1 生成器示例

function* fibonacciSequence() {
let x = 0, y = 1;
for(;;) {
yield y;
[x, y] = [y, x+y];  // Note: destructuring assignment
}
}

// Return the nth Fibonacci number
function fibonacci(n) {
for(let f of fibonacciSequence()) {
if (n-- <= 0) return f;
}
}
fibonacci(20)   // => 10946

// Yield the first n elements of the specified iterable object
function* take(n, iterable) {
let it = iterable[Symbol.iterator](); // Get iterator for iterable object
while(n-- > 0) {           // Loop n times:
let next = it.next();  // Get the next item from the iterator.
if (next.done) return; // If there are no more values, return early
else yield next.value; // otherwise, yield the value
}
}
// An array of the first 5 Fibonacci numbers
[...take(5, fibonacciSequence())]  // => [1, 1, 2, 3, 5]

// Given an array of iterables, yield their elements in interleaved order.
function* zip(...iterables) {
// Get an iterator for each iterable
let iterators = iterables.map(i => i[Symbol.iterator]());
let index = 0;
while(iterators.length > 0) {       // While there are still some iterators
if (index >= iterators.length) {    // If we reached the last iterator
index = 0;                      // go back to the first one.
}
let item = iterators[index].next(); // Get next item from next iterator.
if (item.done) {                    // If that iterator is done
iterators.splice(index, 1);     // then remove it from the array.
}
else {                              // Otherwise,
yield item.value;               // yield the iterated value
index++;                        // and move on to the next iterator.
}
}
}
// Interleave three iterable objects
[...zip(oneDigitPrimes(),"ab",[0])]     // => [2,"a",0,3,"b",5,7]

### 12.3.2 yield* 和递归生成器

function* sequence(...iterables) {
for(let iterable of iterables) {
for(let item of iterable) {
yield item;
}
}
}
[...sequence("abc",oneDigitPrimes())]  // => ["a","b","c",2,3,5,7]

function* sequence(...iterables) {
for(let iterable of iterables) {
yield* iterable;
}
}
[...sequence("abc",oneDigitPrimes())]  // => ["a","b","c",2,3,5,7]

function* sequence(...iterables) {
iterables.forEach(iterable => yield* iterable );  // Error
}

yield*可以与任何类型的可迭代对象一起使用，包括使用生成器实现的可迭代对象。这意味着yield*允许我们定义递归生成器，你可以使用这个特性来允许对递归定义的树结构进行简单的非递归迭代，例如。

## 12.4 高级生成器功能

### 12.4.1 生成器函数的返回值

function *oneAndDone() {
yield 1;
return "done";
}
// The return value does not appear in normal iteration.
[...oneAndDone()]   // => [1]
// But it is available if you explicitly call next()
let generator = oneAndDone();
generator.next()           // => { value: 1, done: false}
generator.next()           // => { value: "done", done: true }
// If the generator is already done, the return value is not returned again
generator.next()           // => { value: undefined, done: true }

### 12.4.2 yield 表达式的值

function* smallNumbers() {
console.log("next() invoked the first time; argument discarded");
let y1 = yield 1;    // y1 == "b"
console.log("next() invoked a second time with argument", y1);
let y2 = yield 2;    // y2 == "c"
console.log("next() invoked a third time with argument", y2);
let y3 = yield 3;    // y3 == "d"
console.log("next() invoked a fourth time with argument", y3);
return 4;
}
let g = smallNumbers();
console.log("generator created; no code runs yet");
let n1 = g.next("a");   // n1.value == 1
console.log("generator yielded", n1.value);
let n2 = g.next("b");   // n2.value == 2
console.log("generator yielded", n2.value);
let n3 = g.next("c");   // n3.value == 3
console.log("generator yielded", n3.value);
let n4 = g.next("d");   // n4 == { value: 4, done: true }
console.log("generator returned", n4.value);

generator created; no code runs yet
next() invoked the first time; argument discarded
generator yielded 1
next() invoked a second time with argument b
generator yielded 2
next() invoked a third time with argument c
generator yielded 3
next() invoked a fourth time with argument d
generator returned 4

## 12.5 总结

• for/of循环和...扩展运算符适用于可迭代对象。
• 如果一个对象有一个名为[Symbol.iterator]的方法返回一个迭代器对象，那么它就是可迭代的。
• 迭代器对象有一个next()方法返回一个迭代结果对象。
• 迭代结果对象有一个value属性，保存下一个迭代的值（如果有的话）。如果迭代已完成，则结果对象必须将done属性设置为true
• 你可以通过定义一个[Symbol.iterator]()方法返回一个具有next()方法返回迭代结果对象的对象来实现自己的可迭代对象。你也可以实现接受迭代器参数并返回迭代器值的函数。
• 生成器函数（使用function*而不是function定义的函数）是定义迭代器的另一种方式。
• 当调用生成器函数时，函数体不会立即运行；相反，返回值是一个可迭代的迭代器对象。每次调用迭代器的next()方法时，生成器函数的另一个块会运行。
• 生成器函数可以使用yield运算符指定迭代器返回的值。每次调用next()都会导致生成器函数运行到下一个yield表达式。该yield表达式的值然后成为迭代器返回的值。当没有更多的yield表达式时，生成器函数返回，迭代完成。

## 13.1 使用回调进行异步编程

### 13.1.1 定时器

setTimeout(checkForUpdates, 60000);

setTimeout()的第一个参数是一个函数，第二个是以毫秒为单位的时间间隔。在上述代码中，一个假设的checkForUpdates()函数将在setTimeout()调用后的 60,000 毫秒（1 分钟）后被调用。checkForUpdates()是你的程序可能定义的回调函数，setTimeout()是你调用以注册回调函数并指定在何种异步条件下调用它的函数。

setTimeout()调用指定的回调函数一次，不传递任何参数，然后忘记它。如果你正在编写一个真正检查更新的函数，你可能希望它重复运行。你可以使用setInterval()而不是setTimeout()来实现这一点：

// Call checkForUpdates in one minute and then again every minute after that
// setInterval() returns a value that we can use to stop the repeated
// invocations by calling clearInterval(). (Similarly, setTimeout()
// returns a value that you can pass to clearTimeout())
clearInterval(updateIntervalId);
}

### 13.1.2 事件

// Ask the web browser to return an object representing the HTML
// <button> element that matches this CSS selector
let okay = document.querySelector('#confirmUpdateDialog button.okay');
// Now register a callback function to be invoked when the user
// clicks on that button.
okay.addEventListener('click', applyUpdate);

JavaScript 权威指南第七版（GPT 重译）（五）(2)https://developer.aliyun.com/article/1485372

|
20天前
|

Python 金融编程第二版（GPT 重译）（四）（4）
Python 金融编程第二版（GPT 重译）（四）
19 3
|
20天前
|

Python 金融编程第二版（GPT 重译）（一）（4）
Python 金融编程第二版（GPT 重译）（一）
20 2
|
20天前
|

Python 金融编程第二版（GPT 重译）（二）（4）
Python 金融编程第二版（GPT 重译）（二）
16 0
|
20天前
|

Python 金融编程第二版（GPT 重译）（二）（3）
Python 金融编程第二版（GPT 重译）（二）
26 0
|
12天前
|
JavaScript 前端开发 程序员
《JavaScript权威指南第7版》中文PDF+英文PDF+源代码 +JavaScript权威指南(第6版)(附源码)PDF下载阅读分享推荐
JavaScript是Web标准语言，广泛应用于各类浏览器，造就了其最广泛部署的地位。Node.js的兴起扩展了JavaScript的使用场景，使其成为开发者首选语言。无论新手还是经验丰富的程序员，都能受益于学习JavaScript。[《JavaScript权威指南第7版》资源链接](https://zhangfeidezhu.com/?p=224)
27 5
|
20天前
|

Python 金融编程第二版（GPT 重译）（四）（5）
Python 金融编程第二版（GPT 重译）（四）
16 2
|
20天前
|

Python 金融编程第二版（GPT 重译）（四）（1）
Python 金融编程第二版（GPT 重译）（四）
21 2
|
20天前
|

Python 金融编程第二版（GPT 重译）（三）（4）
Python 金融编程第二版（GPT 重译）（三）
15 2
|
20天前
|

Python 金融编程第二版（GPT 重译）（一）（1）
Python 金融编程第二版（GPT 重译）（一）
20 1
|
20天前
|
SQL 存储 数据库
Python 金融编程第二版（GPT 重译）（四）（3）
Python 金融编程第二版（GPT 重译）（四）
17 1