站長資訊網
        最全最豐富的資訊網站

        解析JavaScript中的Generators

        最近,為了更好地理解Redux Sagas的工作原理,我重學了JavaScript generators的知識,我把從網上收集到的各種知識點濃縮到一篇文章里,我希望這篇文章既通俗易懂,又足夠嚴謹,可以作為初學者的generators使用指南。

        簡介

        JavaScript在ES6時引入了生成器。生成器函數與常規函數類似,除了可以暫停和恢復它們這一點以外。生成器也與迭代器密切相關,因為生成器對象就是迭代器。
        在JavaScript中,函數調用后通常不能暫?;蛲V?。(是的,異步函數在等待await語句時暫停,但是異步函數在ES7時才引入。此外,異步函數是建立在生成器之上的。)一個普通函數只有在返回或拋出錯誤時才會結束。

        function foo() {   console.log('Starting');   const x = 42;   console.log(x);     console.log('Stop me if you can');     console.log('But you cannot');  }

        相反,生成器允許我們在任意斷點處暫停執行,并從同一斷點恢復執行。

        生成器和迭代器

        來自MDN:

        在JavaScript中,迭代器是一個對象,它定義一個序列,并在終止時可能返回一個返回值。 >更具體地說,迭代器是通過使用 next() 方法實現 Iterator protocol >的任何一個對象,該方法返回具有兩個屬性的對象: value,這是序列中的 next 值;和 done, 如果已經迭代到序列中的最后一個值,則它為 true 。如果 value 和 done 一起存在,則它是迭代器的返回值。

        因此,迭代器的本質就是:

        • 定義序列的對象
        • 有一個next()方法…
        • 返回一個具有兩個屬性的對象:value和done

        是否需要生成器來創建迭代器?不。事實上,我們已經可以使用閉包pre-ES6創建一個無限的斐波那契數列,如下例所示:

        var fibonacci = {   next: (function () {     var pre = 0, cur = 1;     return function () {       tmp = pre;       pre = cur;       cur += tmp;       return cur;     };   })() };  fibonacci.next(); // 1 fibonacci.next(); // 2 fibonacci.next(); // 3 fibonacci.next(); // 5 fibonacci.next(); // 8

        關于生成器的好處,我將再次引用MDN:

        雖然自定義迭代器是一個有用的工具,但是由于需要顯式地維護它們的內部狀態,創建它們需要我們仔細地編程。生成器函數提供了一個強大的替代方法:它們允許我們通過編寫一個執行不是連續的函數來定義迭代算法。
        換句話說,使用生成器創建迭代器更簡單(不需要閉包!),這意味著出錯的可能性更小。
        生成器和迭代器之間的關系就是生成器函數返回的生成器對象是迭代器。

        語法

        生成器函數使用function *語法創建,并使用yield關鍵字暫停。
        最初調用生成器函數并不執行它的任何代碼;相反,它返回一個生成器對象。該值通過調用生成器的next()方法來使用,該方法執行代碼,直到遇到yield關鍵字,然后暫停,直到再次調用next()。

        function * makeGen() {   yield 'Hello';   yield 'World'; }  const g = makeGen(); // g is a generator g.next(); // { value: 'Hello', done: false } g.next(); // { value: 'World', done: false } g.next(); // { value: undefined, done: true }

        在上面的最后一個語句之后重復調用g.next()只會返回(或者更準確地說,產生)相同的返回對象:{ value: undefined, done: true }。

        yield暫停執行

        大家可能會注意到上面的代碼片段有一些特殊之處。第二個next()調用生成一個對象,該對象的屬性為done: false,而不是done: true。
        既然我們正在生成器函數中執行最后一條語句,那么done屬性不應該為true嗎?并不是的。當遇到yield語句時,它后面的值(在本例中是“World”)被生成,執行暫停。因此,第二個next()調用暫停在第二個yield語句上,因此執行還沒有完成—只有在第二個yield語句之后執行重新開始時,執行才算完成(即done: true),并且不再運行代碼。
        我們可以將next()調用看作是告訴程序運行到下一個yield語句(假設它存在)、生成一個值并暫停。程序在恢復執行之前不會知道yield語句之后沒有任何內容,并且只能通過另一個next()調用恢復執行。

        yield和return

        在上面的示例中,我們使用yield將值傳遞給生成器外部。我們也可以使用return(就像在普通函數中一樣);但是,使用return可以終止執行并設置done: true。

        function * makeGen() {   yield 'Hello';   return 'Bye';   yield 'World'; }  const g = makeGen(); // g is a generator g.next(); // { value: 'Hello', done: false } g.next(); // { value: 'Bye', done: true } g.next(); // { value: undefined, done: true }

        因為執行不會在return語句上暫停,而且根據定義,在return語句之后不能執行任何代碼,所以done被設置為true。

        yield:next方法的參數

        到目前為止,我們一直在使用yield傳遞生成器外部的值(并暫停其執行)。
        然而,yield實際上是雙向的,并且允許我們將值傳遞到生成器函數中。

        function * makeGen() {   const foo = yield 'Hello world';   console.log(foo); }  const g = makeGen(); g.next(1); // { value: 'Hello world', done: false } g.next(2); // logs 2, yields { value: undefined, done: true }

        等一下。不應該是"1"打印到控制臺,但是控制臺打印的是"2"?起初,我發現這部分在概念上與直覺相反,因為我預期的賦值foo = 1。畢竟,我們將“1”傳遞到next()方法調用中,從而生成Hello world,對嗎?
        但事實并非如此。傳遞給第一個next(…)調用的值將被丟棄。除了這似乎是ES6規范之外,實際上沒有其他原因.從語義上講,第一個next方法用來啟動遍歷器對象,所以不用帶有參數。
        我喜歡這樣對程序的執行進行合理化:

        • 在第一個next()調用時,它將一直運行,直到遇到yield 'Hello world',在此基礎上生成{ value: 'Hello world', done: false }和暫停。就是這么回事。正如大家所看到的,傳遞給第一個next()調用的任何值都是不會被使用的(因此被丟棄)。
        • 當再次調用next(…)時,執行將恢復。在這種情況下,執行需要為常量foo分配一些值(由yield語句決定)。因此,我們對next(2)的第二次調用賦值foo=2。程序不會在這里停止—它會一直運行,直到遇到下一個yield或return語句。在本例中,沒有
        贊(0)
        分享到: 更多 (0)
        網站地圖   滬ICP備18035694號-2    滬公網安備31011702889846號
        主站蜘蛛池模板: 亚洲精品麻豆av| 国产成人亚洲精品青草天美| 91精品啪在线观看国产| 欧美日韩国产精品自在自线| 狠狠色丁香婷婷综合精品视频| 曰韩精品无码一区二区三区| 狠狠精品干练久久久无码中文字幕| 国产成人精品亚洲日本在线| 日产精品久久久久久久| 亚洲精品无码久久不卡| 久久精品成人免费国产片小草 | 久久99热精品| 精品国产一区二区三区久久久狼 | 麻豆成人久久精品二区三区免费| 久草热久草热线频97精品| 一本大道久久a久久精品综合| 99热这里只有精品在线| 精品国产福利一区二区| 日韩精品一区二区三区中文| 亚洲精品成a人在线观看| 人妻少妇看A偷人无码精品视频| 国产精品手机在线观看你懂的| 97精品人妻一区二区三区香蕉 | 亚洲国产精品久久久天堂| 久久国产精品一区| 国语自产精品视频| 精品久久久久久国产免费了| 国产一区二区三区欧美精品| 99热精品久久只有精品| 国产高清在线精品一区二区| wwwvr高清亚洲精品二区| 777久久精品一区二区三区无码| 色综合久久综精品| 四虎在线精品视频一二区| 欧美精品1区2区| 99RE8这里有精品热视频| 国产成人久久久精品二区三区| 国产精品免费视频观看拍拍| 精品国产亚洲一区二区在线观看| 精品国产亚洲男女在线线电影| 免费人欧美日韩在线精品 |