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.

熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

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

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

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

如何使用WebSocket和JavaScript實現線上語音辨識系統引言:隨著科技的不斷發展,語音辨識技術已成為了人工智慧領域的重要組成部分。而基於WebSocket和JavaScript實現的線上語音辨識系統,具備了低延遲、即時性和跨平台的特點,成為了廣泛應用的解決方案。本文將介紹如何使用WebSocket和JavaScript來實現線上語音辨識系

WebSocket與JavaScript:實現即時監控系統的關鍵技術引言:隨著互聯網技術的快速發展,即時監控系統在各個領域中得到了廣泛的應用。而實現即時監控的關鍵技術之一就是WebSocket與JavaScript的結合使用。本文將介紹WebSocket與JavaScript在即時監控系統中的應用,並給出程式碼範例,詳細解釋其實作原理。一、WebSocket技

如何利用JavaScript和WebSocket實現即時線上點餐系統介紹:隨著網路的普及和技術的進步,越來越多的餐廳開始提供線上點餐服務。為了實現即時線上點餐系統,我們可以利用JavaScript和WebSocket技術。 WebSocket是一種基於TCP協定的全雙工通訊協議,可實現客戶端與伺服器的即時雙向通訊。在即時線上點餐系統中,當使用者選擇菜餚並下訂單

如何使用WebSocket和JavaScript實現線上預約系統在當今數位化的時代,越來越多的業務和服務都需要提供線上預約功能。而實現一個高效、即時的線上預約系統是至關重要的。本文將介紹如何使用WebSocket和JavaScript來實作一個線上預約系統,並提供具體的程式碼範例。一、什麼是WebSocketWebSocket是一種在單一TCP連線上進行全雙工

JavaScript和WebSocket:打造高效的即時天氣預報系統引言:如今,天氣預報的準確性對於日常生活以及決策制定具有重要意義。隨著技術的發展,我們可以透過即時獲取天氣數據來提供更準確可靠的天氣預報。在本文中,我們將學習如何使用JavaScript和WebSocket技術,來建立一個高效的即時天氣預報系統。本文將透過具體的程式碼範例來展示實現的過程。 We

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

Golang是一個快速、有效率的靜態編譯型語言,其簡潔的語法和強大的效能讓它在軟體開發領域備受青睞。在Golang中,迭代器(Iterator)是一種常用的設計模式,用於遍歷集合中的元素而無需暴露集合的內部結構。本文將詳細介紹如何在Golang中實作和使用迭代器,透過具體的程式碼範例幫助讀者更好地理解。 1.迭代器的定義在Golang中,迭代器通常由一個介面和實

如果您渴望找到頂尖的免費AI動畫藝術生成器,您可以結束搜尋了。動漫藝術世界幾十年來一直以其獨特的角色設計、迷人的色彩和引人入勝的情節吸引觀眾。不過,創作動漫藝術需要天份、技能和耗費大量時間。然而,隨著人工智慧(AI)的不斷發展,現在你可以藉助最佳的免費AI動畫藝術生成器,無需深入了解複雜技術,就能探索動漫藝術的世界。這將為你釋放創造力提供新的可能性。什麼是人工智慧動漫藝術生成器? AI動畫藝術生成器利用複雜的演算法和機器學習技術,分析廣泛的動畫作品資料庫。透過這些演算法,系統學習並識別不同動漫風格的
