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