搜尋
首頁web前端js教程JavaScript中的迭代器與生成器詳解_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 中被包含在

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

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

複製程式碼 程式碼如下:

function simpleGenerator(){
      yield "first";
      yield "second";
      yield "third";
      for (var i = 0; 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         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.

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
JavaScript的角色:使網絡交互和動態JavaScript的角色:使網絡交互和動態Apr 24, 2025 am 12:12 AM

JavaScript是現代網站的核心,因為它增強了網頁的交互性和動態性。 1)它允許在不刷新頁面的情況下改變內容,2)通過DOMAPI操作網頁,3)支持複雜的交互效果如動畫和拖放,4)優化性能和最佳實踐提高用戶體驗。

C和JavaScript:連接解釋C和JavaScript:連接解釋Apr 23, 2025 am 12:07 AM

C 和JavaScript通過WebAssembly實現互操作性。 1)C 代碼編譯成WebAssembly模塊,引入到JavaScript環境中,增強計算能力。 2)在遊戲開發中,C 處理物理引擎和圖形渲染,JavaScript負責遊戲邏輯和用戶界面。

從網站到應用程序:JavaScript的不同應用從網站到應用程序:JavaScript的不同應用Apr 22, 2025 am 12:02 AM

JavaScript在網站、移動應用、桌面應用和服務器端編程中均有廣泛應用。 1)在網站開發中,JavaScript與HTML、CSS一起操作DOM,實現動態效果,並支持如jQuery、React等框架。 2)通過ReactNative和Ionic,JavaScript用於開發跨平台移動應用。 3)Electron框架使JavaScript能構建桌面應用。 4)Node.js讓JavaScript在服務器端運行,支持高並發請求。

Python vs. JavaScript:比較用例和應用程序Python vs. JavaScript:比較用例和應用程序Apr 21, 2025 am 12:01 AM

Python更適合數據科學和自動化,JavaScript更適合前端和全棧開發。 1.Python在數據科學和機器學習中表現出色,使用NumPy、Pandas等庫進行數據處理和建模。 2.Python在自動化和腳本編寫方面簡潔高效。 3.JavaScript在前端開發中不可或缺,用於構建動態網頁和單頁面應用。 4.JavaScript通過Node.js在後端開發中發揮作用,支持全棧開發。

C/C在JavaScript口譯員和編譯器中的作用C/C在JavaScript口譯員和編譯器中的作用Apr 20, 2025 am 12:01 AM

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。1)C 用于解析JavaScript源码并生成抽象语法树。2)C 负责生成和执行字节码。3)C 实现JIT编译器,在运行时优化和编译热点代码,显著提高JavaScript的执行效率。

JavaScript在行動中:現實世界中的示例和項目JavaScript在行動中:現實世界中的示例和項目Apr 19, 2025 am 12:13 AM

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

JavaScript和Web:核心功能和用例JavaScript和Web:核心功能和用例Apr 18, 2025 am 12:19 AM

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

了解JavaScript引擎:實施詳細信息了解JavaScript引擎:實施詳細信息Apr 17, 2025 am 12:05 AM

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。