<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                ## 35 同步生成器(高級) > 原文: [http://exploringjs.com/impatient-js/ch_sync-generators.html](http://exploringjs.com/impatient-js/ch_sync-generators.html) > > 貢獻者:[steve000](https://github.com/steve000) ### 35.1 什么是同步生成器? 同步生成器是函數定義和方法定義的特殊版本,它們始終返回同步可迭代: ```js // Generator function declaration function* genFunc1() { /*···*/ } // Generator function expression const genFunc2 = function* () { /*···*/ }; // Generator method definition in an object literal const obj = { * generatorMethod() { // ··· } }; // Generator method definition in a class definition // (class declaration or class expression) class MyClass { * generatorMethod() { // ··· } } ``` 星號(`*`)將函數和方法標記為生成器: * 功能:偽關鍵字`function*`是關鍵字`function`和星號的組合。 * 方法:`*`是一個修飾符(類似于`static`和`get`)。 #### 35.1.1 生成器函數返回 iterables 并通過`yield`填充它們 如果調用生成器函數,它將返回一個 iterable(實際上:也是可迭代的迭代器)。生成器通過`yield`運算符填充可迭代的: ```js function* genFunc1() { yield 'a'; yield 'b'; } const iterable = genFunc1(); // Convert the iterable to an Array, to check what’s inside: assert.deepEqual([...iterable], ['a', 'b']); // You can also use a for-of loop for (const x of genFunc1()) { console.log(x); } // Output: // 'a' // 'b' ``` #### 35.1.2 `yield`暫停生成器功能 到目前為止,`yield`看起來像是一種向迭代中添加值的簡單方法。但是,它做的遠不止于此 - 它還會暫停和退出生成器功能: * 與`return`類似,`yield`退出函數體。 * 與`return`不同,如果再次調用該函數,則會在`yield`之后直接執行。 讓我們通過以下生成器函數來檢查它的含義。 ```js let location = 0; function* genFunc2() { location = 1; yield 'a'; location = 2; yield 'b'; location = 3; } ``` 生成器函數的結果稱為 _ 生成器對象 _。它不僅僅是一個可迭代的,但這超出了本書的范圍(如果您對更多細節感興趣,請參考[“探索 ES6”](http://exploringjs.com/es6/ch_generators.html))。 為了使用`genFunc2()`,我們必須首先創建生成器對象`genObj`。 `genFunc2()`現在暫停“身體前”。 ```js const genObj = genFunc2(); // genFunc2() is now paused “before” its body: assert.equal(location, 0); ``` `genObj`實現[迭代協議](ch_sync-iteration.html)。因此,我們通過`genObj.next()`控制`genFunc2()`的執行。調用該方法,恢復暫停的`genFunc2()`并執行它直到有`yield`。然后執行暫停,`.next()`返回`yield`的操作數: ```js assert.deepEqual( genObj.next(), {value: 'a', done: false}); // genFunc2() is now paused directly after the first `yield`: assert.equal(location, 1); ``` 請注意,產生的值`'a'`包裝在一個對象中,這就是迭代總是傳遞它們的值的方式。 我們再次調用`genObj.next()`并繼續執行我們先前暫停的位置。一旦遇到第二個`yield`,`genFunc2()`暫停,`.next()`返回產生的值`'b'`。 ```js assert.deepEqual( genObj.next(), {value: 'b', done: false}); // genFunc2() is now paused directly after the second `yield`: assert.equal(location, 2); ``` 我們再一次調用`genObj.next()`并繼續執行直到它離開`genFunc2()`的主體: ```js genObj.next(), {value: undefined, done: true}); // We have reached the end of genFunc2(): assert.equal(location, 3); ``` 這次,`.next()`的結果的屬性`.done`是`true`,這意味著可迭代完成。 #### 35.1.3 為什么`yield`暫停執行? `yield`暫停執行有什么好處?為什么它不像 Array 方法`.push()`那樣工作并用值填充 iterable - 沒有暫停? 由于暫停,生成器提供 _ 協同程序 _ 的許多功能(認為協同多任務的進程)。例如,當你要求迭代的下一個值時,該值被計算 _ 懶惰 _(按需)。以下兩個生成器函數演示了這意味著什么。 ```js /** * Returns an iterable over lines */ function* genLines() { yield 'A line'; yield 'Another line'; yield 'Last line'; } /** * Input: iterable over lines * Output: iterable over numbered lines */ function* numberLines(lineIterable) { let lineNumber = 1; for (const line of lineIterable) { // input yield lineNumber + ': ' + line; // output lineNumber++; } } ``` 請注意,`numberLines()`內的`yield`出現在`for-of`循環內。 `yield`可以在循環內部使用,但不能在回調內部使用(稍后會詳細介紹)。 讓我們結合兩個生成器來生成可迭代的`numberedLines`: ```js const numberedLines = numberLines(genLines()); assert.deepEqual( numberedLines.next(), {value: '1: A line', done: false}); assert.deepEqual( numberedLines.next(), {value: '2: Another line', done: false}); ``` 每次我們通過`.next()`向`numberedLines`詢問另一個值時,`numberLines()`只詢問`genLines()`一行并給它編號。如果`genLines()`同步從大文件中讀取它的行,我們將能夠從文件中讀取第一個編號行。如果`yield`沒有暫停,我們必須等到`genLines()`完全讀完。 ![](https://img.kancloud.cn/3e/d5/3ed5755d562179ae6c199264f5e21157.svg) **練習:將正常功能轉換為發生器** `exercises/generators/fib_seq_test.js` #### 35.1.4 示例:迭代迭代 以下函數`mapIter()`類似于`Array.from()`,但它返回一個可迭代的,而不是一個數組,并根據需要生成其結果。 ```js function* mapIter(iterable, func) { let index = 0; for (const x of iterable) { yield func(x, index); index++; } } const iterable = mapIter(['a', 'b'], x => x + x); assert.deepEqual([...iterable], ['aa', 'bb']); ``` ![](https://img.kancloud.cn/3e/d5/3ed5755d562179ae6c199264f5e21157.svg) **練習:過濾迭代** `exercises/generators/filter_iter_gen_test.js` ### 35.2 從生成器調用生成器(高級) #### 35.2.1 通過`yield*`調用生成器 `yield`只能直接在生成器中工作 - 到目前為止,我們還沒有看到將屈服委托給另一個函數或方法的方法。 讓我們首先檢查 _ 不是 _ 的工作原理:在下面的例子中,我們希望`foo()`調用`bar()`,以便后者為前者產生兩個值。唉,天真的方法失敗了: ```js function* foo() { // Nothing happens if we call `bar()`: bar(); } function* bar() { yield 'a'; yield 'b'; } assert.deepEqual( [...foo()], []); ``` 為什么這不起作用?函數調用`bar()`返回一個我們忽略的 iterable。 我們想要的是`foo()`產生`bar()`產生的所有東西。這就是`yield*`運算符的作用: ```js function* foo() { yield* bar(); } function* bar() { yield 'a'; yield 'b'; } assert.deepEqual( [...foo()], ['a', 'b']); ``` 換句話說,之前的`foo()`大致相當于: ```js function* foo() { for (const x of bar()) { yield x; } } ``` 請注意,`yield*`適用于任何可迭代: ```js function* gen() { yield* [1, 2]; } assert.deepEqual( [...gen()], [1, 2]); ``` #### 35.2.2 示例:在樹上迭代 `yield*`允許我們在生成器中進行遞歸調用,這在迭代遞歸數據結構(如樹)時很有用。舉例來說,二叉樹的數據結構如下。 ```js class BinaryTree { constructor(value, left=null, right=null) { this.value = value; this.left = left; this.right = right; } /** Prefix iteration: parent before children */ * [Symbol.iterator]() { yield this.value; if (this.left) { // Same as yield* this.left[Symbol.iterator]() yield* this.left; } if (this.right) { yield* this.right; } } } ``` 方法`[Symbol.iterator]()`增加了對迭代協議的支持,這意味著我們可以使用`for-of`循環迭代`BinaryTree`的實例: ```js const tree = new BinaryTree('a', new BinaryTree('b', new BinaryTree('c'), new BinaryTree('d')), new BinaryTree('e')); for (const x of tree) { console.log(x); } // Output: // 'a' // 'b' // 'c' // 'd' // 'e' ``` ![](https://img.kancloud.cn/3e/d5/3ed5755d562179ae6c199264f5e21157.svg) **練習:迭代嵌套數組** `exercises/generators/iter_nested_arrays_test.js` ### 35.3 示例:重用循環 生成器的一個重要用例是提取和重用循環功能。 #### 35.3.1 要重用的循環 作為一個例子,考慮以下函數迭代文件樹并記錄它們的路徑(它使用 [Node.js API](https://nodejs.org/en/docs/) 這樣做): ```js function logFiles(dir) { for (const fileName of fs.readdirSync(dir)) { const filePath = path.resolve(dir, fileName); console.log(filePath); const stats = fs.statSync(filePath); if (stats.isDirectory()) { logFiles(filePath); // recursive call } } } const rootDir = process.argv[2]; logFiles(rootDir); ``` 我們如何重用這個循環來做除記錄路徑之外的其他事情? #### 35.3.2 內部迭代(推送) 重用迭代代碼的一種方法是通過 [_ 內部迭代 _](ch_arrays.html#external-iteration-internal-iteration) :將每個迭代值傳遞給回調(A 行)。 ```js function iterFiles(dir, callback) { for (const fileName of fs.readdirSync(dir)) { const filePath = path.resolve(dir, fileName); callback(filePath); // (A) const stats = fs.statSync(filePath); if (stats.isDirectory()) { iterFiles(filePath, callback); } } } const rootDir = process.argv[2]; const paths = []; iterFiles(rootDir, p => paths.push(p)); ``` #### 35.3.3 外部迭代(拉) 另一種重用迭代代碼的方法是通過 [_ 外部迭代 _](ch_arrays.html#external-iteration-internal-iteration) :我們可以編寫一個生成所有迭代值的生成器。 ```js function* iterFiles(dir) { for (const fileName of fs.readdirSync(dir)) { const filePath = path.resolve(dir, fileName); yield filePath; // (A) const stats = fs.statSync(filePath); if (stats.isDirectory()) { yield* iterFiles(filePath); } } } const rootDir = process.argv[2]; const paths = [...iterFiles(rootDir)]; ``` ### 35.4 生成器的高級功能 * `yield`也可以通過`.next()` _ 接收 _ 數據。有關詳細信息,請參閱[“探索 ES6”](http://exploringjs.com/es6/ch_generators.html#sec_generators-as-observers)。 * `yield*`的結果是其操作數返回的結果。有關詳細信息,請參閱[“探索 ES6”](http://exploringjs.com/es6/ch_generators.html#_recursion-via-yield)。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看