在JavaScript中使用高階函數(shù)的方法
將另一個(gè)函數(shù)作為參數(shù)的函數(shù),或者定義一個(gè)函數(shù)作為返回值的函數(shù),被稱(chēng)為高階函數(shù)。
JavaScript可以接受高階函數(shù)。這種處理高階函數(shù)的能力以及其他特點(diǎn),使JavaScript成為非常適合函數(shù)式編程的編程語(yǔ)言之一。
JavaScript將函數(shù)視為一等公民
你也許聽(tīng)說(shuō)過(guò),JavaScript函數(shù)是一等公民。這意味著,在JavaScript中函數(shù)是對(duì)象。
它們的類(lèi)型是Object
,它們可以作為一個(gè)變量的值被分配,而且它們可以像其他引用變量一樣被傳遞和返回。
一等函數(shù)賦予了JavaScript特殊的能力,使我們能夠從高階函數(shù)中獲益。
由于函數(shù)是對(duì)象,且JavaScript是流行的編程語(yǔ)言之一,因此其支持函數(shù)式編程的原生方法。
事實(shí)上,一等函數(shù)是JavaScript的原生方法。我敢打賭你在使用他們的時(shí)候甚至都沒(méi)有想過(guò)正在使用函數(shù)。
高階函數(shù)接收函數(shù)作為參數(shù)
如果你做過(guò)很多JavaScript開(kāi)發(fā),你可能遇到過(guò)使用回調(diào)函數(shù)的情況。
回調(diào)函數(shù)是一個(gè)在操作結(jié)束時(shí)執(zhí)行的函數(shù),一旦所有其他操作完成后便會(huì)執(zhí)行。
通常情況下,我們把這個(gè)函數(shù)作為最后的參數(shù)傳遞,在其他參數(shù)之后。它通常被定義為內(nèi)聯(lián)的匿名函數(shù)?;卣{(diào)函數(shù)依靠的是JavaScript處理高階函數(shù)的能力。
JavaScript是一個(gè)單線(xiàn)程語(yǔ)言。這意味著同一時(shí)間只有一個(gè)操作會(huì)被執(zhí)行。
為了避免操作或系統(tǒng)的主線(xiàn)程互相阻塞(這將導(dǎo)致死鎖),引擎會(huì)確保所有操作按順序執(zhí)行。它們沿著這個(gè)單線(xiàn)程排隊(duì),直到安全產(chǎn)生另一個(gè)代碼事務(wù)。
將一個(gè)函數(shù)作為參數(shù)傳入,并在父函數(shù)的其他操作完成后運(yùn)行該函數(shù)的能力,對(duì)于支持高階函數(shù)的語(yǔ)言來(lái)說(shuō)是至關(guān)重要的。
JavaScript中的回調(diào)函數(shù)允許異步行為,因此腳本可以在等待結(jié)果的同時(shí)繼續(xù)執(zhí)行其他函數(shù)或操作。
在處理可能在不確定的時(shí)間段后返回結(jié)果的資源時(shí),傳遞回調(diào)函數(shù)的能力至關(guān)重要。
這種高階函數(shù)模式在網(wǎng)絡(luò)開(kāi)發(fā)中非常有用。一個(gè)腳本可以向服務(wù)器發(fā)送一個(gè)請(qǐng)求,然后需要在響應(yīng)到來(lái)時(shí)進(jìn)行處理,而不需要了解服務(wù)器的網(wǎng)絡(luò)延遲或處理時(shí)間。
Node.js經(jīng)常使用回調(diào)函數(shù)來(lái)有效地利用服務(wù)器資源。這種異步方法對(duì)于等待用戶(hù)輸入后再執(zhí)行函數(shù)的應(yīng)用程序來(lái)說(shuō)也很有用。
考慮一下這個(gè)簡(jiǎn)單的JavaScript片段,它為一個(gè)按鈕添加了一個(gè)事件監(jiān)聽(tīng)器。
document.getElementById("clicker").addEventListener("click", function() { alert("you triggered " + this.id); });
這段腳本使用內(nèi)聯(lián)匿名函數(shù)來(lái)顯示一個(gè)alert
。
但它也可以很容易地使用一個(gè)單獨(dú)定義的函數(shù),并將這個(gè)命名函數(shù)傳遞給addEventListener
方法。
var proveIt = function() { alert("you triggered " + this.id); }; document.getElementById("clicker").addEventListener("click", proveIt);
我們這樣做不僅僅是展示了高階函數(shù)。我們使代碼更可讀,更有彈性,并為不同的任務(wù)分離了功能(監(jiān)聽(tīng)點(diǎn)擊事件與提醒用戶(hù))。
代碼可重用性
我們的proveIt()
函數(shù)在結(jié)構(gòu)上獨(dú)立于它周?chē)拇a,總是返回被觸發(fā)的元素的id
。這種函數(shù)設(shè)計(jì)的方法是函數(shù)式編程的核心。
這段代碼可以存在于任何你用元素的id
顯示alert
的上下文中,并且可以被任何事件監(jiān)聽(tīng)器調(diào)用。
用一個(gè)單獨(dú)定義和命名的函數(shù)取代內(nèi)聯(lián)函數(shù)的能力為我們提供了無(wú)限可能。
在函數(shù)式編程中,我們?cè)噲D開(kāi)發(fā)不改變外部數(shù)據(jù)的純函數(shù),并且每次對(duì)相同的輸入返回相同的結(jié)果。
現(xiàn)在我們有了一個(gè)基本的工具,可以幫助我們開(kāi)發(fā)一個(gè)小型的、有針對(duì)性的高階函數(shù)庫(kù),你可以在任何應(yīng)用程序中使用。
請(qǐng)注意,我們把 proveIt
而不是 proveIt()
傳遞給我們的 addEventListener
函數(shù)。
- 當(dāng)你不帶括號(hào)傳遞一個(gè)函數(shù)的名字時(shí),你傳遞的是函數(shù)對(duì)象本身。
- 當(dāng)你用圓括號(hào)傳遞函數(shù)時(shí),你是在傳遞執(zhí)行該函數(shù)的結(jié)果。
返回函數(shù)
除了將函數(shù)作為參數(shù)之外,JavaScript還允許函數(shù)將其他函數(shù)作為結(jié)果返回。
這是說(shuō)得通的,因?yàn)楹瘮?shù)是簡(jiǎn)單的對(duì)象。對(duì)象(包括函數(shù))可以被定義為一個(gè)函數(shù)的返回值,就像字符串、數(shù)組或其他值。
但是函數(shù)作為結(jié)果返回是什么意思呢?
函數(shù)是分解問(wèn)題和創(chuàng)建可重用代碼片斷的一種強(qiáng)大方式。當(dāng)我們將一個(gè)函數(shù)定義為一個(gè)高階函數(shù)的返回值時(shí),它可以作為新函數(shù)的模板。
假如你讀了太多關(guān)于"千禧一代"的文章,感到厭煩。你決定每當(dāng)出現(xiàn)"千禧一代"這個(gè)詞時(shí),你都要用 "蛇人"這個(gè)短語(yǔ)來(lái)代替它。
你可能是簡(jiǎn)單地寫(xiě)一個(gè)函數(shù),在你傳遞給它的任何文本上執(zhí)行該文本替換。
var snakify = function(text) { return text.replace(/millenials/ig, "Snake People"); }; console.log(snakify("The Millenials are always up to something.")); // The Snake People are always up to something.
這種寫(xiě)法是有效的,但不夠通用。你可能想為其他情況寫(xiě)一個(gè)替換函數(shù):
var hippify = function(text) { return text.replace(/baby boomers/ig, "Aging Hippies"); }; console.log(hippify("The Baby Boomers just look the other way.")); // The Aging Hippies just look the other way.
但是,如果你決定要做一些更復(fù)雜的事情來(lái)保留原始字符串中的大小寫(xiě)呢?你將不得不修改你的兩個(gè)新函數(shù)來(lái)做到這一點(diǎn)。
這很麻煩,而且會(huì)使你的代碼更加脆弱,也更難閱讀。在這樣的情況下,我們可以使用高階函數(shù)作為解決方案。
高階函數(shù)模板
你真正想要的是能夠在模板函數(shù)中用任何其他術(shù)語(yǔ)替換任何術(shù)語(yǔ)的靈活性,并將該行為定義為一個(gè)基礎(chǔ)函數(shù),你可以在此基礎(chǔ)上建立新的自定義函數(shù)。
有了將函數(shù)指定為返回值的能力,JavaScript提供了讓這種情況更便捷的方法:
var attitude = function(original, replacement, source) { return function(source) { return source.replace(original, replacement); }; }; var snakify = attitude(/millenials/ig, "Snake People"); var hippify = attitude(/baby boomers/ig, "Aging Hippies"); console.log(snakify("The Millenials are always up to something.")); // The Snake People are always up to something. console.log(hippify("The Baby Boomers just look the other way.")); // The Aging Hippies just look the other way.
我們所做的是把做實(shí)際工作的代碼隔離到一個(gè)通用的、可擴(kuò)展的attitude
函數(shù)中。它封裝了所有需要修改任何輸入字符串的工作:使用原始短語(yǔ)作為初始值,并輸出一個(gè)具有某種態(tài)度的替換短語(yǔ)。
當(dāng)我們將這個(gè)新函數(shù)定義為對(duì)attitude
高階函數(shù)的引用,并預(yù)先填入它所接收的前兩個(gè)參數(shù)時(shí),我們會(huì)得到什么?它允許新函數(shù)接收你傳遞給它的任何文本,并在我們定義的返回函數(shù)中使用該參數(shù)作為attitude
函數(shù)的輸出。
JavaScript函數(shù)不關(guān)心傳遞給它們的參數(shù)的數(shù)量。
如果缺少第二個(gè)參數(shù),函數(shù)將把它視為undefined
。當(dāng)我們選擇不提供第三個(gè)參數(shù),或任何數(shù)量的額外參數(shù)時(shí),它也會(huì)這樣做。
此外,你可以在以后再傳入那個(gè)額外的參數(shù)。你可以在定義了你想調(diào)用的高階函數(shù)后這樣做,就像剛才演示的那樣。
我們正在創(chuàng)建一個(gè)模板高階函數(shù)來(lái)返回另一個(gè)函數(shù)。然后,我們把這個(gè)新返回的函數(shù),除去一個(gè)屬性,定義為模板函數(shù)的一個(gè)自定義實(shí)現(xiàn)。
你以這種方式創(chuàng)建的所有函數(shù)將繼承高階函數(shù)的工作代碼。然而,你可以用不同的默認(rèn)參數(shù)預(yù)先定義它們。
正在使用高階函數(shù)
高階函數(shù)對(duì)于JavaScript的工作方式來(lái)說(shuō)是起碼的,你已經(jīng)在使用它們了。
每當(dāng)你傳遞一個(gè)匿名函數(shù)或回調(diào)函數(shù)時(shí),你實(shí)際上是把所傳遞的函數(shù)返回的值,作為另一個(gè)函數(shù)的參數(shù)(如箭頭函數(shù))使用。
開(kāi)發(fā)人員在學(xué)習(xí)JavaScript的早期就熟悉高階函數(shù)。它是JavaScript設(shè)計(jì)中固有的,所以以后才需要學(xué)習(xí)驅(qū)動(dòng)箭頭函數(shù)或回調(diào)的概念。
為返回其他函數(shù)的函數(shù)賦值的能力擴(kuò)展了JavaScript的便利性。高階函數(shù)允許我們創(chuàng)建自定義命名的函數(shù),用一階函數(shù)的共享模板代碼執(zhí)行專(zhuān)門(mén)的任務(wù)。
這些函數(shù)中的每一個(gè)都可以繼承高階函數(shù)中的任何改進(jìn)。這可以協(xié)助我們避免代碼重復(fù),并保持我們的源代碼的整潔和可讀性。
如果你確保你的函數(shù)是純凈的(它們不改變外部值,并且對(duì)于任何給定的輸入總是返回相同的值),你可以創(chuàng)建測(cè)試來(lái)驗(yàn)證當(dāng)你更新一階函數(shù)時(shí),你的代碼變化不會(huì)破壞任何東西。
總結(jié)
現(xiàn)在你知道了高階函數(shù)的工作原理,你可以開(kāi)始考慮如何在自己的項(xiàng)目中利用這個(gè)概念了。
JavaScript的一個(gè)好處是,你可以將函數(shù)技術(shù)與你已經(jīng)熟悉的代碼混合在一起。
即便你一開(kāi)始只是為了使用高階函數(shù)而使用,你也會(huì)很快熟悉它們所提供的額外靈活性。
現(xiàn)在使用高階函數(shù)的一點(diǎn)工作可以在未來(lái)幾年內(nèi)改善你的代碼。
到此這篇關(guān)于如何在JavaScript中使用高階函數(shù)的文章就介紹到這了,更多相關(guān)js高階函數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用webpack/gulp構(gòu)建TypeScript項(xiàng)目的方法示例
這篇文章主要介紹了使用webpack/gulp構(gòu)建TypeScript項(xiàng)目的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12原生JS實(shí)現(xiàn)響應(yīng)式瀑布流布局
本文給大家分享的是使用原生的javascript實(shí)現(xiàn)的響應(yīng)式的瀑布流布局的方法和源碼,非常的實(shí)用,有需要的小伙伴可以參考下。2015-04-04詳解JavaScript 浮點(diǎn)數(shù)運(yùn)算的精度問(wèn)題
這篇文章主要介紹了詳解JavaScript 浮點(diǎn)數(shù)運(yùn)算的精度問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07Js查找字符串中出現(xiàn)次數(shù)最多的字符及個(gè)數(shù)實(shí)例解析
這篇文章主要介紹了Js查找字符串中出現(xiàn)次數(shù)最多的字符及個(gè)數(shù) ,本文分為傳統(tǒng)寫(xiě)法和正則寫(xiě)法兩種方法給大家介紹了js查找字符串出現(xiàn)次數(shù)最多的字符及個(gè)數(shù),非常不錯(cuò),感興趣的朋友參考下吧2016-09-09CocosCreator通用框架設(shè)計(jì)之網(wǎng)絡(luò)
這篇文章主要介紹了CocosCreator通用框架設(shè)計(jì)之網(wǎng)絡(luò),詳細(xì)講解了WebSocket的原理和使用方法,對(duì)WebSocket感興趣的同學(xué),一定要看一下2021-04-04