核心要點
Array
對象的map()
和reduce()
方法是強大的函數式編程工具,可以使代碼更簡潔、易讀且易於維護。 map()
是基本的函數式編程技術,它作用於數組中的所有元素,並生成另一個長度相同的數組,其中包含轉換後的內容。通過向數組對象添加映射功能,ECMAScript 5 將基本的數組類型變成了一個完整的函子,使函數式編程更容易上手。 reduce()
方法(也是ECMAScript 5中的新增方法)類似於map()
,但它不是生成另一個函子,而是生成單個結果,該結果可以是任何類型。它按順序將函數應用於數組的每個元素,以便將數組簡化為單個輸出值。 map()
和reduce()
方法可能會影響性能,但如今使用它們帶來的代碼質量和開發人員滿意度的提升,很可能超過對性能的任何暫時影響。作者建議使用函數式技術進行開發,並在實際情況下衡量影響,然後再決定map()
和reduce()
是否適合某個應用程序。 ECMAScript 6 的眾多令人驚嘆的新特性相關的流程討論甚囂塵上,很容易讓人忘記ECMAScript 5 為我們在JavaScript 中支持函數式編程提供了一些很棒的工具,而這些工具我們今天就可以使用。其中包括基於 JavaScript Array
對象的原生 map()
和 reduce()
方法。
如果您現在還沒有使用 map()
和 reduce()
,那麼現在就開始吧!大多數現代 JavaScript 平台都原生支持 ECMAScript 5。映射和規約可以使您的代碼更簡潔、更易於閱讀和維護,並引導您走向更優雅的函數式開發。
性能:注意事項
當然,在需要的情況下,必須將代碼的閱讀和維護與性能相平衡。目前,瀏覽器使用更繁瑣的傳統技術(例如for
循環)的效率更高。
我的方法通常是首先編寫易於閱讀和維護的代碼,然後如果我在實際情況下注意到問題,則優化性能。過早優化是萬惡之源。
還值得考慮的是,隨著瀏覽器對 map()
和 reduce()
進行優化,使用這些方法可能會更好地利用 JavaScript 引擎的改進。除非我面臨性能問題,否則我更喜歡樂觀地編寫代碼,並將使我的代碼不那麼吸引人的性能調整放在我的後備口袋裡,以防我需要它們。
使用 Map
映射是作用於數組中所有元素並生成另一個長度相同的數組(包含轉換後的內容)的基本函數式編程技術。
為了更具體一些,讓我們想出一個簡單的用例。例如,假設您有一個單詞數組,您需要將其轉換為包含每個單詞長度的數組。 (我知道,這並不是您通常需要為複雜的應用程序執行的那種複雜的火箭科學,但是理解它在這種簡單情況下的工作原理將幫助您在它可以為您的代碼增加實際價值的情況下應用它)。
您可能已經知道如何使用數組上的for
循環來完成我剛才描述的操作。它可能看起來像這樣:
<code class="language-javascript">var animals = ["cat","dog","fish"]; var lengths = []; var item; var count; var loops = animals.length; for (count = 0; count < loops; count++) { item = animals[count]; lengths.push(item.length); } console.log(lengths); //[3, 3, 4]</code>
我們所做的只是定義了一些變量:一個名為animals
的數組,其中包含我們的單詞;一個名為lengths
的空數組,它將包含我們操作的輸出;以及一個名為item
的變量,用於臨時存儲我們將要在數組的每個循環中操作的每個項目。我們使用一個帶有臨時內部計數器變量的for
循環和一個loops
變量來優化我們的for
循環。然後,我們迭代了直到 animals
數組長度的每個項目。對於每一個項目,我們計算其長度,並將其推送到 lengths
數組中。
注意:可以說,我們可以通過直接將 animals[count]
的長度推送到 lengths
數組而無需中間賦值來更簡潔地完成此操作。這將節省我們一些代碼,但也會使代碼的可讀性降低,即使對於這個非常簡單的示例也是如此。同樣,為了使它更有效率,但不太直接,我們可以使用已知的 animals
數組長度來初始化我們的 lengths
數組為 new Array(animals.length)
,然後通過索引插入項目而不是使用 push
。這完全取決於您將在現實世界中如何使用代碼。
這種方法在技術上沒有任何問題。它應該在任何標準的 JavaScript 引擎中都能工作,並且它將完成工作。但是,一旦您知道如何使用 map()
,這樣做就會顯得笨拙。
讓我向您展示我們如何使用 map()
來處理這個問題:
<code class="language-javascript">var animals = ["cat","dog","fish"]; var lengths = animals.map(function(animal) { return animal.length; }); console.log(lengths); //[3, 3, 4]</code>
在這種情況下,我們再次從 animals
數組變量開始。但是,我們聲明的唯一其他變量是 lengths
,我們將它的值直接賦值給將匿名內聯函數映射到 animals
數組的每個元素的結果。該匿名函數對每個動物執行操作,返回長度。結果,lengths
成為與原始 animals
數組長度相同的數組,包含每個單詞的長度。
關於這種方法需要注意幾點。首先,它比原來的方法短得多。其次,我們必須聲明的變量要少得多。變量越少,全局命名空間中的噪聲就越少,如果同一代碼的其他部分使用相同名稱的變量,則衝突的機會就越少。此外,我們的變量從頭到尾都不需要更改其值。當您深入研究函數式編程時,您將欣賞使用常量和不可變變量的優雅能力,而且現在就開始學習也為時不晚。
這種方法的另一個優點是,我們有機會通過分離命名函數來提高其多功能性,從而在過程中生成更簡潔的代碼。匿名內聯函數可能看起來很凌亂,並且使代碼重用變得更加困難。我們可以定義一個名為 getLength()
的函數,並通過這種方式在上下文中使用它:
<code class="language-javascript">var animals = ["cat","dog","fish"]; var lengths = []; var item; var count; var loops = animals.length; for (count = 0; count < loops; count++) { item = animals[count]; lengths.push(item.length); } console.log(lengths); //[3, 3, 4]</code>
看看這看起來多麼乾淨?僅僅將映射作為您的工具包的一部分就可以將您的代碼提升到一個全新的函數式級別。
有趣的是,通過向數組對象添加映射功能,ECMAScript 5 將基本的數組類型變成了一個完整的函子,使函數式編程更容易為我們所有人所用。
根據經典的函數式編程定義,函子滿足三個條件:
這是在您下次 JavaScript 聚會上可以討論的一個話題。
如果您想了解更多關於函子的信息,請查看 Mattias Petter Johansson 的這個精彩視頻。
使用 Reduce
reduce()
方法也是 ECMAScript 5 中的新增方法,它類似於 map()
,不同之處在於它不是生成另一個函子,而是生成單個結果,該結果可以是任何類型。例如,假設您想將 animals
數組中所有單詞的長度之和作為一個數字。您可能會從以下操作開始:
<code class="language-javascript">var animals = ["cat","dog","fish"]; var lengths = animals.map(function(animal) { return animal.length; }); console.log(lengths); //[3, 3, 4]</code>
在定義初始數組後,我們為運行總計創建一個變量 total
,最初設置為零。我們還創建了一個變量 item
來保存 animals
數組在遍歷for
循環時的每次迭代,以及一個變量 count
用於循環計數器,以及一個 loops
變量來優化我們的迭代。然後,我們運行一個for
循環來迭代 animals
數組中的所有單詞,並將每個單詞賦值給 item
變量。最後,我們將每個項目的長度添加到我們的總計中。
同樣,這種方法在技術上沒有任何問題。我們從一個數組開始,最終得到一個結果。但是,使用 reduce()
方法,我們可以使這個過程更直接:
<code class="language-javascript">var animals = ["cat","dog","fish"]; var lengths = []; var item; var count; var loops = animals.length; for (count = 0; count < loops; count++) { item = animals[count]; lengths.push(item.length); } console.log(lengths); //[3, 3, 4]</code>
這裡發生的情況是,我們正在定義一個新變量 total
,並將其賦值給使用兩個參數減少 animals
數組的結果:一個匿名內聯函數和一個初始運行總計值零。減少會遍歷數組中的每個項目,對該項目執行函數,並將其添加到傳遞給下一次迭代的運行總計中。這裡我們的內聯函數有兩個參數:運行總和和當前正在從數組中處理的單詞。該函數將 total
的當前值添加到當前單詞的長度。
請注意,我們將 reduce()
的第二個參數設置為零,這表明 total
將包含一個數字。如果沒有第二個參數,reduce
方法仍然可以工作,但結果不一定是您期望的結果。 (試一試,看看當省略運行總計時,JavaScript 使用的邏輯是什麼。)
由於在調用 reduce()
方法時集成了匿名內聯函數的定義,這可能看起來比需要復雜一點。讓我們再次這樣做,但讓我們首先定義一個命名函數,而不是使用匿名內聯函數:
<code class="language-javascript">var animals = ["cat","dog","fish"]; var lengths = animals.map(function(animal) { return animal.length; }); console.log(lengths); //[3, 3, 4]</code>
這個稍微長一些,但長並不總是壞事。以這種方式查看它應該使 reduce
方法發生的事情更清晰一些。
reduce()
方法有兩個參數:一個應用於數組中每個元素的函數,以及一個用於運行總計的初始值。在這種情況下,我們傳遞一個名為 addLength
的新函數的名稱和運行總計的初始值零。我們創建了 addLength()
函數,以便它也接受兩個參數:運行總和和要處理的字符串。
結論
養成定期使用map()
和reduce()
的習慣將為您提供替代方法,使您的代碼更簡潔、更通用、更易於維護,並為您鋪平使用更多函數式JavaScript 技術的道路。
map()
和 reduce()
方法只是添加到 ECMAScript 5 的新方法中的兩個。很可能,您今天使用它們所看到的代碼質量和開發人員滿意度的提高將遠遠超過對性能的任何暫時影響。使用函數式技術進行開發,並在現實世界中衡量影響,然後再決定 map()
和 reduce()
是否適合您的應用程序。
本文由 Panayiotis Velisarakos、Tim Severien 和 Dan Prince 共同評審。感謝所有 SitePoint 的同行評審者,使 SitePoint 內容達到最佳狀態!
關於函數式 JavaScript 中 Map-Reduce 的常見問題 (FAQ)
在 JavaScript 中,Map 和 Reduce 都是作用於數組的高階函數。 Map 函數用於通過將函數應用於原始數組的每個元素來創建一個新數組。它不會修改原始數組,而是返回一個新數組。另一方面,Reduce 函數用於將數組簡化為單個值。它按順序將函數應用於數組的每個元素,以便將其簡化為單個輸出值。
JavaScript 中的 Map 函數通過從現有數組創建一個新數組來工作。它通過將指定的函數應用於原始數組中的每個元素來實現此目的。該函數按順序為數組中的每個元素調用一次。結果是一個包含函數調用結果的新數組。
JavaScript 中的 Reduce 函數通過按順序將函數應用於數組中的每個元素,以便將數組簡化為單個輸出值來工作。輸出值是函數調用的累積結果。該函數接受兩個參數:累加器和當前值。累加器累積函數調用的返回值。
是的,您可以在 JavaScript 中同時使用 Map 和 Reduce。事實上,它們經常在函數式編程中一起使用。您可以使用 Map 函數轉換數組中的每個元素,然後使用 Reduce 函數將轉換後的元素組合成單個輸出值。
Map 和 ForEach 都是 JavaScript 中作用於數組的高階函數。它們之間的主要區別在於,Map 通過將函數應用於原始數組的每個元素來創建一個新數組,而 ForEach 應用於數組的每個元素以獲得其副作用。 ForEach 不會返回新數組。
您可以使用 JavaScript 中的 Map 函數通過將函數應用於數組的每個元素來轉換數組。該函數按順序為數組中的每個元素調用一次。結果是一個包含函數調用結果的新數組。
您可以使用 JavaScript 中的 Reduce 函數將數組的元素組合成單個輸出值。該函數按順序應用於數組中的每個元素,輸出值是函數調用的累積結果。
JavaScript 中的 Map 函數通常用於通過將函數應用於數組的每個元素來轉換數組。一些常見用例包括將字符串轉換為數字、更改字符串的大小寫以及從對像中提取屬性。
JavaScript 中的 Reduce 函數通常用於將數組的元素組合成單個輸出值。一些常見用例包括求和數字、查找最大值或最小值以及連接字符串。
您可以通過在函數中使用 console.log
語句來顯示變量和表達式的值來調試 JavaScript 中的 Map 或 Reduce 函數。您還可以使用 debugger
語句暫停執行並在 Web 瀏覽器的開發者工具中檢查變量和表達式的值。
以上是使用地圖並減少功能性JavaScript的詳細內容。更多資訊請關注PHP中文網其他相關文章!