亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 語言 > JavaScript > 正文

JavaScript中的迭代器和生成器詳解

2024-05-06 16:10:08
字體:
來源:轉載
供稿:網友
這篇文章主要介紹了JavaScript中的迭代器和生成器詳解,本文講解了迭代器、聲明自定義迭代器、生成器:一種更好的方式來構建迭代器、生成器高級特性等內容,需要的朋友可以參考下
 
 

處理集合里的每一項是一個非常普通的操作,JavaScript提供了許多方法來迭代一個集合,從簡單的for和for each循環到 map(),filter() 和 array comprehensions(數組推導式)。在JavaScript 1.7中,迭代器和生成器在JavaScript核心語法中帶來了新的迭代機制,而且還提供了定制 for…in 和 for each 循環行為的機制。

迭代器

迭代器是一個每次訪問集合序列中一個元素的對象,并跟蹤該序列中迭代的當前位置。在JavaScript中迭代器是一個對象,這個對象提供了一個 next() 方法,next() 方法返回序列中的下一個元素。當序列中所有元素都遍歷完成時,該方法拋出 StopIteration 異常。

迭代器對象一旦被建立,就可以通過顯式的重復調用next(),或者使用JavaScript的 for…in 和 for each 循環隱式調用。

簡單的對對象和數組進行迭代的迭代器可以使用 Iterator() 被創建:

 

復制代碼代碼如下:

var lang = { name: 'JavaScript', birthYear: 1995 };
    var it = Iterator(lang);

 

一旦初始化完成,next() 方法可以被調用來依次訪問對象的鍵值對:

 

復制代碼代碼如下:

  var pair = it.next(); //鍵值對是["name", "JavaScript"]
    pair = it.next(); //鍵值對是["birthday", 1995]
    pair = it.next(); //一個 `StopIteration` 異常被拋出

 

for…in 循環可以被用來替換顯式的調用 next() 方法。當 StopIteration 異常被拋出時,循環會自動終止。

 

復制代碼代碼如下:

 var it = Iterator(lang);
    for (var pair in it)
      print(pair); //每次輸出 it 中的一個 [key, value] 鍵值對

 

如果你只想迭代對象的 key 值,可以往 Iterator() 函數中傳入第二個參數,值為 true:

 

復制代碼代碼如下:

  var it = Iterator(lang, true);
    for (var key in it)
      print(key); //每次輸出 key 值

 

使用 Iterator() 訪問對象的一個好處是,被添加到 Object.prototype 的自定義屬性不會被包含在序列對象中。

Iterator() 同樣可以被作用在數組上:

 

復制代碼代碼如下:

var langs = ['JavaScript', 'Python', 'Haskell'];
    var it = Iterator(langs);
    for (var pair in it)
      print(pair); //每次迭代輸出 [index, language] 鍵值對

 

就像遍歷對象一樣,把 true 當做第二個參數傳入遍歷的結果將會是數組索引:

 

復制代碼代碼如下:

 var langs = ['JavaScript', 'Python', 'Haskell'];
    var it = Iterator(langs, true);
    for (var i in it)
      print(i); //輸出 0,然后是 1,然后是 2

 

使用 let 關鍵字可以在循環內部分別分配索引和值給塊變量,還可以解構賦值(Destructuring Assignment):

 

復制代碼代碼如下:

 var langs = ['JavaScript', 'Python', 'Haskell'];
    var it = Iterators(langs);
    for (let [i, lang] in it)
      print(i + ': ' + lang); //輸出 "0: JavaScript" 等

 

聲明自定義迭代器

一些代表元素集合的對象應該用一種指定的方式來迭代。

1.迭代一個表示范圍(Range)的對象應該一個接一個的返回這個范圍包含的數字
2.一個樹的葉子節點可以使用深度優先或者廣度優先訪問到
3.迭代一個代表數據庫查詢結果的對象應該一行一行的返回,即使整個結果集尚未全部加載到一個單一數組
4.作用在一個無限數學序列(像斐波那契序列)上的迭代器應該在不創建無限長度數據結構的前提下一個接一個的返回結果

JavaScript 允許你寫自定義迭代邏輯的代碼,并把它作用在一個對象上

我們創建一個簡單的 Range 對象,包含低和高兩個值:

 

復制代碼代碼如下:

function Range(low, high){
      this.low = low;
      this.high = high;
    }

 

現在我們創建一個自定義迭代器,它返回一個包含范圍內所有整數的序列。迭代器接口需要我們提供一個 next() 方法用來返回序列中的下一個元素或者是拋出 StopIteration 異常。

 

復制代碼代碼如下:

 function RangeIterator(range){
      this.range = range;
      this.current = this.range.low;
    }
    RangeIterator.prototype.next = function(){
      if (this.current > this.range.high)
        throw StopIteration;
      else
        return this.current++;
    };

 

我們的 RangeIterator 通過 range 實例來實例化,同時維持一個 current 屬性來跟蹤當前序列的位置。

最后,為了讓 RangeIterator 可以和 Range 結合起來,我們需要為 Range 添加一個特殊的 __iterator__ 方法。當我們試圖去迭代一個 Range 時,它將被調用,而且應該返回一個實現了迭代邏輯的 RangeIterator 實例。

 

復制代碼代碼如下:

Range.prototype.__iterator__ = function(){
      return new RangeIterator(this);
    };

 

完成我們的自定義迭代器后,我們就可以迭代一個范圍實例:

 

復制代碼代碼如下:

var range = new Range(3, 5);
    for (var i in range)
      print(i); //輸出 3,然后 4,然后 5

 

生成器:一種更好的方式來構建迭代器

雖然自定義的迭代器是一種很有用的工具,但是創建它們的時候要仔細規劃,因為需要顯式的維護它們的內部狀態。

生成器提供了很強大的功能:它允許你定義一個包含自有迭代算法的函數, 同時它可以自動維護自己的狀態。

生成器是可以作為迭代器工廠的特殊函數。如果一個函數包含了一個或多個 yield 表達式,那么就稱它為生成器(譯者注:Node.js 還需要在函數名前加 * 來表示)。

注意:只有 HTML 中被包含在 <script type="application/javascript;version=1.7"> (或者更高版本)中的代碼塊才可以使用 yield 關鍵字。XUL (XML User Interface Language) 腳本標簽不需要指定這個特殊的代碼塊也可以訪問這些特性。

當一個生成器函數被調用時,函數體不會即刻執行,它會返回一個 generator-iterator 對象。每次調用 generator-iterator 的 next() 方法,函數體就會執行到下一個 yield 表達式,然后返回它的結果。當函數結束或者碰到 return 語句,一個 StopIteration 異常會被拋出。

用一個例子來更好的說明:

 

復制代碼代碼如下:

function simpleGenerator(){
      yield "first";
      yield "second";
      yield "third";
      for (var i = 0; i < 3; i++)
        yield i;
    }
    
    var g = simpleGenerator();
    print(g.next()); //輸出 "first"
    print(g.next()); //輸出 "second"
    print(g.next()); //輸出 "third"
    print(g.next()); //輸出 0
    print(g.next()); //輸出 1
    print(g.next()); //輸出 2
    print(g.next()); //拋出 StopIteration 異常

 

生成器函數可以被一個類直接的當做 __iterator__ 方法使用,在需要自定義迭代器的地方可以有效的減少代碼量。我們使用生成器重寫一下 Range :

復制代碼代碼如下:

function Range(low, high){
      this.low = low;
      this.high = high;
    }
    Range.prototype.__iterator__ = function(){
      for (var i = this.low; i <= this.high; i++)
        yield i;
    };
    var range = new Range(3, 5);
    for (var i in range)
      print(i); //輸出 3,然后 4,然后 5

 

不是所有的生成器都會終止,你可以創建一個代表無限序列的生成器。下面的生成器實現一個斐波那契序列,就是每一個元素都是前面兩個的和:

復制代碼代碼如下:

function fibonacci(){
      var fn1 = 1;
      var fn2 = 1;
      while (1) {
        var current = fn2;
        fn2 = fn1;
        fn1 = fn1 + current;
        yield current;
      }
    }
    
    var sequence = fibonacci();
    print(sequence.next()); // 1
    print(sequence.next()); // 1
    print(sequence.next()); // 2
    print(sequence.next()); // 3
    print(sequence.next()); // 5
    print(sequence.next()); // 8
    print(sequence.next()); // 13

 

生成器函數可以帶有參數,并且會在第一次調用函數時使用這些參數。生成器可以被終止(引起它拋出 StopIteration 異常)通過使用 return 語句。下面的 fibonacci() 變體帶有一個可選的 limit 參數,當條件被觸發時終止函數。

 

復制代碼代碼如下:

function fibonacci(limit){
      var fn1 = 1;
      var fn2 = 1;
      while(1){
        var current = fn2;
        fn2 = fn1;
        fn1 = fn1 + current;
        if (limit && current > limit)
          return;
        yield current;
      }
    }

 

生成器高級特性

生成器可以根據需求計算yield返回值,這使得它可以表示以前昂貴的序列計算需求,甚至是上面所示的無限序列。

除了 next() 方法,generator-iterator 對象還有一個 send() 方法,該方法可以修改生成器的內部狀態。傳給 send() 的值將會被當做最后一個 yield 表達式的結果,并且會暫停生成器。在你使用 send() 方法傳一個指定值前,你必須至少調用一次 next() 來啟動生成器。

下面的斐波那契生成器使用 send() 方法來重啟序列:

 

復制代碼代碼如下:

 function fibonacci(){
      var fn1 = 1;
      var fn2 = 1;
      while (1) {
        var current = fn2;
        fn2 = fn1;
        fn1 = fn1 + current;
        var reset = yield current;
        if (reset) {
          fn1 = 1;
          fn2 = 1;
        }
      }
    }
    
    var sequence = fibonacci();
    print(sequence.next());     //1
    print(sequence.next());     //1
    print(sequence.next());     //2
    print(sequence.next());     //3
    print(sequence.next());     //5
    print(sequence.next());     //8
    print(sequence.next());     //13
    print(sequence.send(true)); //1
    print(sequence.next());     //1
    print(sequence.next());     //2
    print(sequence.next());     //3

 

注意:有意思的一點是,調用 send(undefined) 和調用 next() 是完全同等的。不過,當調用 send() 方法啟動一個新的生成器時,除了 undefined 其它的值都會拋出一個 TypeError 異常。

你可以調用 throw 方法并且傳遞一個它應該拋出的異常值來強制生成器拋出一個異常。此異常將從當前上下文拋出并暫停生成器,類似當前的 yield 執行,只不過換成了 throw value 語句。

如果在拋出異常的處理過程中沒有遇到 yield ,該異常將會被傳遞直到調用 throw() 方法,并且隨后調用 next() 將會導致 StopIteration 異常被拋出。

生成器擁有一個 close() 方法來強制生成器結束。結束一個生成器會產生如下影響:

1.所有生成器中有效的 finally 字句將會執行
2.如果 finally 字句拋出了除 StopIteration 以外的任何異常,該異常將會被傳遞到 close() 方法的調用者
3.生成器會終止

生成器表達式

數組推導式 的一個明顯缺點是,它們會導致整個數組在內存中構造。當輸入到推導式的本身是個小數組時它的開銷是微不足道的—但是,當輸入數組很大或者創建一個新的昂貴(或者是無限的)數組生成器時就可能出現問題。

生成器允許對序列延遲計算(lazy computation),在需要時按需計算元素。生成器表達式在句法上幾乎和數組推導式相同—它用圓括號來代替方括號(而且用 for...in 代替 for each...in)—但是它創建一個生成器而不是數組,這樣就可以延遲計算。你可以把它想象成創建生成器的簡短語法。

假設我們有一個迭代器 it 來迭代一個巨大的整數序列。我們需要創建一個新的迭代器來迭代偶數。一個數組推導式將會在內存中創建整個包含所有偶數的數組:

復制代碼代碼如下:

var doubles = [i * 2 for (i in it)];

 

而生成器表達式將會創建一個新的迭代器,并且在需要的時候按需來計算偶數值:

復制代碼代碼如下:

 var it2 = (i * 2 for (i in it));
    print(it2.next());  //it 里面的第一個偶數
    print(it2.next());  //it 里面的第二個偶數

 

當一個生成器被用做函數的參數,圓括號被用做函數調用,意味著最外層的圓括號可以被省略:

 

復制代碼代碼如下:

var result = doSomething(i * 2 for (i in it));

 

End.


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久久精品欧美| 亚洲欧美一区二区三区四区| 精品日本高清在线播放| 欧美激情亚洲精品| 精品日韩视频在线观看| 亚洲自拍欧美另类| 91产国在线观看动作片喷水| 国语自产精品视频在线看抢先版图片| 亚洲日韩第一页| 国产69久久精品成人看| 亚洲成色777777在线观看影院| 欧美性猛交xxxx黑人猛交| 中文字幕av一区二区| 久久中文字幕一区| 国产97色在线|日韩| 亚洲第一综合天堂另类专| 一本大道亚洲视频| 亚洲毛片在线免费观看| 搡老女人一区二区三区视频tv| 国产精品久久av| 亚洲第一区第一页| 国产一区二区三区欧美| 在线观看不卡av| 97成人超碰免| 久久99精品久久久久久青青91| 亚洲自拍偷拍在线| 亚洲国产成人在线播放| 欧美日韩国产成人| 亚洲国产欧美一区二区三区同亚洲| www.久久久久久.com| 国产日韩在线亚洲字幕中文| 欧美成人午夜影院| 欧美黑人性猛交| 18性欧美xxxⅹ性满足| 久久男人资源视频| 91久久久国产精品| 成人国产精品色哟哟| 久久久女女女女999久久| 欧美老少配视频| 国产精品久久久久久五月尺| 青青久久aⅴ北条麻妃| 久久最新资源网| 亚洲精品国产精品国产自| 国产激情视频一区| 国产亚洲欧美日韩一区二区| 国产精品极品美女粉嫩高清在线| 日韩禁在线播放| 国产精品久久久久免费a∨| 久久久国产精品视频| www国产亚洲精品久久网站| 91精品国产99久久久久久| 欧美激情视频网| 中文字幕在线亚洲| 4438全国亚洲精品在线观看视频| 国产成人福利视频| 国产一区二区色| 欧美在线观看网址综合| 最新中文字幕亚洲| 日韩视频―中文字幕| 亚洲开心激情网| 久久躁狠狠躁夜夜爽| 国产精品久久久久久久久久久久久久| 亚洲精品日韩丝袜精品| 久久精品欧美视频| 国产精品一区二区三区久久久| 亚洲精品日韩丝袜精品| 亚洲va久久久噜噜噜久久天堂| 日韩中文字幕国产精品| 欧美精品福利在线| 久热爱精品视频线路一| 日韩电影免费观看在线| 91日本在线视频| 91久久精品国产91久久| 久久久精品国产一区二区| 欧美国产第二页| 日韩在线不卡视频| 色偷偷av一区二区三区| 懂色av一区二区三区| 在线视频一区二区| 亚洲自拍偷拍视频| 午夜免费久久久久| 亚洲精品久久久久中文字幕二区| 亚洲国产精品视频在线观看| 亚洲91精品在线| 日韩av在线最新| 久久综合88中文色鬼| 国产精品亚洲精品| 国产精品九九九| 亚洲free性xxxx护士白浆| 亚洲精品久久久久中文字幕欢迎你| 久久综合伊人77777| 久久影院资源站| 欧美日韩国产影院| 日韩中文字幕欧美| 久热国产精品视频| 国产日本欧美一区| 国产精品永久免费视频| 亚洲国产91精品在线观看| 在线观看欧美视频| 久久国产精品久久久久| 久久人人爽人人爽人人片亚洲| 亚洲精品视频二区| 欧美在线视频在线播放完整版免费观看| 中文字幕免费国产精品| 国产99久久精品一区二区永久免费| 国产一区二区三区四区福利| 九九热最新视频//这里只有精品| 在线播放国产一区中文字幕剧情欧美| 日本高清+成人网在线观看| 成人免费黄色网| 国产精品va在线播放我和闺蜜| 国产精品一区二区3区| 亚洲第一色中文字幕| 久久免费高清视频| 孩xxxx性bbbb欧美| 国产99久久精品一区二区永久免费| 在线观看日韩av| 久久久综合av| 午夜精品一区二区三区在线视| 国产一区二区黄| 91精品国产乱码久久久久久久久| 成人夜晚看av| 91精品国产自产91精品| 国产99在线|中文| 亚洲一区二区三区视频播放| 欧美孕妇毛茸茸xxxx| 91久久精品美女高潮| 欧美在线影院在线视频| 欧美丝袜一区二区| 国模精品系列视频| 欧美日韩高清在线观看| 欧美在线中文字幕| 91av在线播放视频| 91精品国产91久久久久| 91精品国产网站| 国产精品网红福利| 尤物精品国产第一福利三区| 在线一区二区日韩| 亚洲精选一区二区| 国产日韩欧美黄色| 日本高清视频精品| 激情成人在线视频| 欧美中文在线观看国产| 久久中文字幕在线| 免费av一区二区| 久久久久久亚洲精品不卡| 亚洲理论片在线观看| 欧美精品在线极品| 国产欧美va欧美va香蕉在线| 色老头一区二区三区在线观看| 综合激情国产一区| 久久97久久97精品免视看| 久久久在线免费观看| 亚洲国产免费av| 最新国产成人av网站网址麻豆| 久久久久久久999精品视频| 亚洲精品suv精品一区二区| 欧美性高跟鞋xxxxhd| 麻豆国产精品va在线观看不卡| 国产精品久久久一区| 国产成人小视频在线观看| 亚洲在线观看视频| 中文欧美日本在线资源| 欧美视频裸体精品|