摘要:簡介柯里化,又稱部分求值,是把接收多個(gè)參數(shù)的函數(shù)變成接受一個(gè)單一參數(shù)最初函數(shù)的第一個(gè)參數(shù)的函數(shù),并且返回接受剩余的參數(shù)而且返回結(jié)果的新函數(shù)的技術(shù)。按照作者的說法,所謂柯里化就是使函數(shù)理解并處理部分應(yīng)用。的思想極大地助于提升函數(shù)的復(fù)用性。
簡介
柯里化(Currying),又稱部分求值(Partial Evaluation),是把接收多個(gè)參數(shù)的函數(shù)變成接受一個(gè)單一參數(shù)(最初函數(shù)的第一個(gè)參數(shù))的函數(shù),并且返回接受剩余的參數(shù)而且返回結(jié)果的新函數(shù)的技術(shù)。
核心思想: 把多參數(shù)傳入的函數(shù)拆成單參數(shù)(或部分參數(shù))函數(shù),內(nèi)部再返回調(diào)用下一個(gè)單參數(shù)(或部分參數(shù))函數(shù),依次處理剩余的參數(shù)。
按照Stoyan Stefanov --《JavaScript Pattern》作者 的說法,所謂柯里化就是使函數(shù)理解并處理部分應(yīng)用。
在JavaScript中實(shí)現(xiàn)Currying為了實(shí)現(xiàn)只傳遞給函數(shù)一部分參數(shù)來調(diào)用它,讓它返回一個(gè)函數(shù)去處理剩余參數(shù)的這句話所描述的特征。我們先實(shí)現(xiàn)一個(gè)加法函數(shù)add:
function add(x, y) { return x + y }
我們現(xiàn)在實(shí)現(xiàn)一個(gè)被Currying的add函數(shù),命名該函數(shù)為curriedAdd,則根據(jù)上面的定義,curriedAdd需要滿足以下條件:
curriedAdd(1)(3) === 4 // true var increment = curriedAdd(1) increment(2) === 3 // true var addTen = curriedAdd(10) addTen(2) === 12 // true
滿足以上條件的curriedAdd函數(shù)可以用以下代碼實(shí)現(xiàn):
function curriedAdd(x) { return function(y) { return x + y } }
當(dāng)然以上實(shí)現(xiàn)有一些問題: 它不通用,并且我們并不想通過修改函數(shù)被人的方式來實(shí)現(xiàn)Currying化。
但是curriedAdd的實(shí)現(xiàn)表明了實(shí)現(xiàn)Currying的一個(gè)基礎(chǔ)--Currying延遲求值的特性需要我們用到JavaScript中的作用域,說得更通俗一些,我們需要使用作用域(即閉包)來保存上一次傳進(jìn)來的參數(shù)。
對(duì)curriedAdd進(jìn)行抽象,可以得到如下函數(shù)currying:
function currying (fn, ...args1) { return function (...args2) { return fn(...arg1, ...arg2) } } var increment = currying(add, 1) increment(2) === 3 // true var addTen = currying(add, 10) addTen(2) === 12 // true
在此實(shí)現(xiàn)中,currying函數(shù)的返回值其實(shí)是一個(gè)接受剩余參數(shù)并且立即返回計(jì)算值的函數(shù)。即它的返回值并沒有自動(dòng)被Currying。所以我們可以通過遞歸將currying返回的函數(shù)也自動(dòng)Currying。
function currying(fn, ...args) { if (args.length >= fn.length) { return fn(...args) } return function (...args2) { return currying(fn, ...args, ...args2) } }
以上函數(shù)很簡短,但是已經(jīng)實(shí)現(xiàn)Currying的核心思想。JavaScript中常用庫Lodash中的curry方法,其核心思想和以上并沒有太大差異--比較多次接收的參數(shù)總數(shù)與函數(shù)定義時(shí)的形參數(shù)量,當(dāng)接收的參數(shù)的數(shù)量大于或者等于被Currying函數(shù)的形參數(shù)量時(shí),就返回運(yùn)行結(jié)果,否則返回一個(gè)繼續(xù)接受參數(shù)的函數(shù)。
Currying應(yīng)用場景 參數(shù)復(fù)用固定不變的參數(shù),實(shí)現(xiàn)參數(shù)復(fù)用是Currying的主要用途之一。
案例一上文中的increment、addTen的一個(gè)參數(shù)復(fù)用的實(shí)例。對(duì)add方法固定第一個(gè)參數(shù)為10后,該方法就變成了一個(gè)將接受累加10的方法。
案例二判斷對(duì)象的類型。例如下面這個(gè)例子:
function isArray (obj) { return Object.prototype.toString.call(obk) === "[object Array]" } function isNumber (obj) { return Object.prototype.toString.call(obj) === "[object Number]" } function isString (obj) { return Object.prototype.toString.call(obj) === "[object String]" } // Test isArray([1, 2, 3]) // true isNumber(123) // true isString("123") // true
但是上面方案有一個(gè)問題,那就是每種類型都需要定義一個(gè)方法,這里我們可以使用bind來擴(kuò)展,優(yōu)點(diǎn)是可以直接使用改造后的toStr:
const toStr = Function.prototype.call.bind(Object.prototype.toString) // 改造前直接調(diào)用 [1, 2, 3].toString() // "1,2,3" "123".toString() // "123" 123.toString() // SyntaxError: Invalid or unexpected token Object(123).toString() // "123" // 改造后調(diào)用 toStr toStr([1, 2, 3]) // "[object Array]" toStr("123") // "[object String]" toStr(123) // "[object Number]" toStr(Object(123)) // "[object Number]"
上面例子首先使用Function.prototype.call函數(shù)指定一個(gè)this值,然后.bind返回一個(gè)新的函數(shù),始終將Object.prototype.toString設(shè)置為傳入?yún)?shù),其實(shí)等價(jià)于 Object.prototype.toString.call()。
延遲執(zhí)行延遲執(zhí)行也是Currying的一個(gè)重要使用場景,同樣bind和箭頭函數(shù)也能實(shí)現(xiàn)同樣的功能。
在前端開發(fā)中,一個(gè)常見的場景就是為標(biāo)簽綁定onClick,同時(shí)考慮為綁定的方法傳遞參數(shù)。
以下列出了幾種常見的方法,來比較優(yōu)劣:
通過data屬性本質(zhì)只能傳遞字符串的數(shù)據(jù),如果需要傳遞復(fù)雜對(duì)象,只能通過 JSON.stringify(data)來傳遞滿足JSON對(duì)象格式的數(shù)據(jù),但對(duì)更加復(fù)雜的對(duì)象無法支持。(雖然大多數(shù)時(shí)候也無需傳遞復(fù)雜對(duì)象)
通過bind方法bind方法和以上實(shí)現(xiàn)的currying 方法,在功能上有極大的相似,在實(shí)現(xiàn)上也幾乎差不多。可能唯一的不同就是bind方法需要強(qiáng)制綁定context,也就是bind的第一個(gè)參數(shù)會(huì)作為原函數(shù)運(yùn)行時(shí)的this指向。而currying不需要此參數(shù)。所以使用currying或者bind只是一個(gè)取舍問題。
箭頭函數(shù)handleOnClick(data))} />箭頭函數(shù)能夠?qū)崿F(xiàn)延遲執(zhí)行,同時(shí)也不像bind方法必需指定context。
通過currying性能對(duì)比為什么不需要 Currying 1. Currying 的一些特性有其他解決方案
通過jsPerf測試四種方式的性能,結(jié)果為:箭頭函數(shù) > bind > currying > trueCurrying。
currying函數(shù)相比bind函數(shù),其原理相似,但是性能相差巨大,其原因是bind由瀏覽器實(shí)現(xiàn),運(yùn)行效率有加成。如果我們只是想提前綁定參數(shù),那么我們有很多好幾個(gè)現(xiàn)成的選擇,bind,箭頭函數(shù)等,而且性能比Curring更好。
2. Currying 陷于函數(shù)式編程Currying是函數(shù)式編程的產(chǎn)物,它生于函數(shù)式編程,也服務(wù)于函數(shù)式編程。
而JavaScript并非真正的函數(shù)式編程語言,相比Haskell等函數(shù)式編程語言,JavaScript 使用Currying等函數(shù)式特性有額外的性能開銷,也缺乏類型推導(dǎo)。
從而把JavaScript代碼寫得符合函數(shù)式編程思想和規(guī)范的項(xiàng)目都較少,從而也限制了 Currying等技術(shù)在JavaScript代碼中的普遍使用。
結(jié)論Currying在JavaScript中是低性能的,但是這些性能在絕大多數(shù)場景,是可以忽略的。
Currying的思想極大地助于提升函數(shù)的復(fù)用性。
Currying 生于函數(shù)式編程,也陷于函數(shù)式編程。假如沒有準(zhǔn)備好寫純正的函數(shù)式代碼,那么Currying有更好的替代品。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/106900.html
摘要:組合的概念是非常直觀的,并不是函數(shù)式編程獨(dú)有的,在我們生活中或者前端開發(fā)中處處可見。其實(shí)我們函數(shù)式編程里面的組合也是類似,函數(shù)組合就是一種將已被分解的簡單任務(wù)組織成復(fù)雜的整體過程。在函數(shù)式編程的世界中,有這樣一種很流行的編程風(fēng)格。 JavaScript函數(shù)式編程,真香之認(rèn)識(shí)函數(shù)式編程(一) 該系列文章不是針對(duì)前端新手,需要有一定的編程經(jīng)驗(yàn),而且了解 JavaScript 里面作用域,閉...
摘要:里也有柯里化的實(shí)現(xiàn),只是平時(shí)沒有在意。如果函數(shù)柯里化后雖然生搬硬套,不過現(xiàn)實(shí)業(yè)務(wù)也會(huì)有類似場景。 柯里化 先解釋下什么是 柯里化 在計(jì)算機(jī)科學(xué)中,柯里化(英語:Currying),又譯為卡瑞化或加里化,是把接受多個(gè)參數(shù)的函數(shù)變換成接受一個(gè)單一參數(shù)(最初函數(shù)的第一個(gè)參數(shù))的函數(shù),并且返回接受余下的參數(shù)而且返回結(jié)果的新函數(shù)的技術(shù)。 js 里也有柯里化的實(shí)現(xiàn),只是平時(shí)沒有在意。先把原文簡介貼...
摘要:忍者秘籍一書中,對(duì)于柯里化的定義如下在一個(gè)函數(shù)中首先填充幾個(gè)參數(shù)然后再返回一個(gè)新函數(shù)的技術(shù)稱為柯里化。回到我們的題目本身,其實(shí)根據(jù)測試用例我們可以發(fā)現(xiàn),函數(shù)的要求就是接受單一函數(shù),例如但是與柯里化不同之處在于,柯里化返回的一個(gè)新函數(shù)。 歡迎大家再一次來到我的文章專欄:從面試題中我們能學(xué)到什么,各位同行小伙伴是否已經(jīng)開始了悠閑的春節(jié)假期呢?在這里提前祝大家雞年大吉吧~哈哈,之前有人說...
摘要:一個(gè)經(jīng)常會(huì)看到的函數(shù)的實(shí)現(xiàn)為第一版我們可以這樣使用或者或者已經(jīng)有柯里化的感覺了,但是還沒有達(dá)到要求,不過我們可以把這個(gè)函數(shù)用作輔助函數(shù),幫助我們寫真正的函數(shù)。 JavaScript 專題系列第十三篇,講解函數(shù)柯里化以及如何實(shí)現(xiàn)一個(gè) curry 函數(shù) 定義 維基百科中對(duì)柯里化 (Currying) 的定義為: In mathematics and computer science, cu...
摘要:原文鏈接和都支持函數(shù)的柯里化函數(shù)的柯里化還與的函數(shù)編程有很大的聯(lián)系如果你感興趣的話可以在這些方面多下功夫了解相信收獲一定很多看本篇文章需要知道的一些知識(shí)點(diǎn)函數(shù)部分的閉包高階函數(shù)不完全函數(shù)文章后面有對(duì)這些知識(shí)的簡單解釋大家可以看看什么是柯里化 原文鏈接 Haskell和scala都支持函數(shù)的柯里化,JavaScript函數(shù)的柯里化還與JavaScript的函數(shù)編程有很大的聯(lián)系,如果你感興...
閱讀 3558·2021-08-31 09:39
閱讀 1865·2019-08-30 13:14
閱讀 2927·2019-08-30 13:02
閱讀 2776·2019-08-29 13:22
閱讀 2352·2019-08-26 13:54
閱讀 777·2019-08-26 13:45
閱讀 1594·2019-08-26 11:00
閱讀 989·2019-08-26 10:58