javascript
深入理解javascript系列(十七):函数柯里化
之前的系列,我們介紹了什么是高階函數。所有以函數作為參數的函數,都可以叫作高階函數。并且我們常常利用高階函數來封裝一些公共邏輯。
本次,我們要繼續學習,繼續記錄,柯里化??吕锘?#xff0c;其實就是高階函數的一種特殊用法。
柯里化是指這樣一個函數(假設叫做createCurry),它接收函數A作為參數,運行后能夠返回一個新的函數,并且這個新的函數能夠處理函數A的剩余參數。
文字總是不那么好去理解,下面我們就通過例子來理解吧。
假設有一個接收三個參數的函數A。
function A(a, b, c) {// to do something }復制代碼又假設我們有一個已經封裝好了的柯里化通用函數createCurry。他接收bar作為參數,能夠將A轉化為柯里化函數,返回結果就是這個被轉化之后的函數。
var _A = createCurry(A);復制代碼那么_A作為createCurry運行的返回函數,能夠處理A的剩余參數。因此下面的運行結果都是等價的。
_A(1, 2, 3); _A(1,2)(3); _A(1)(2,3); _A(1)(2)(3); A(1,2,3);復制代碼函數A被createCurry轉化之后得到柯里化函數_A,_A能夠處理A的所有剩余參數。因此柯里化也被稱為部分求值。
在簡單的場景下,我們可以不借助柯里化通用式來轉化得到柯里化函數,僅憑借眼力自己封裝。
例如,有一個簡單的加法函數,它能夠將自身的三個參數加起來并返回計算結果。
function add(a, b, c) {return a + b + c; }復制代碼那么add函數的柯里化函數_add則可以寫成:
function _add(a) {return function(b) {return function(c) {return a + b + c;}} }復制代碼因此下面的運算方式是等價的。
add(1, 2, 3); _add(1)(2)(3);復制代碼當然,柯里化通用式具備更加強大的能力,僅靠眼力勁可不行。因此我們更需要知道如何封裝這樣一個柯里化的通用式。
首先通過_add可以看出,柯里化函數的運行過程其實是一個參數收集過程,我們將每一次傳入的參數收集起來,并在最里層進行處理。因此在實現createCurry時,可以借助這個思路來進行封裝。
代碼如下:
// arity 用來標記剩余參數的個數 // args 用來收集參數function createCurry(func, arity, args) {//第一次執行時,并不會傳入arity,而是直接獲取func參數的個數 func.lengthvar arity = arity || func.length;//第一次執行也不會傳入args,而是默認為空數組var args = args || [];var wrapper = function() {//將wrapper中的參數收集到args中var _args = [].slice.call(arguments);[].push.apply(args, _args);//如果參數個數小于最初的func.length,則遞歸調用,繼續收集參數if(_args.length < arity) {arity -= _args.length;return createCurry(func, arity, args);}//參數收集完畢,執行funcreturn func.apply(func, args);}return wrapper; }復制代碼是不是有些不太容易理解,所以要多閱讀幾次。這個createCurry的封裝其實是借助了閉包和遞歸,實現一個參數收集,并在收集完畢之后執行所有參數。
不知道您是否有發現,函數經過createCurry轉化為一個柯里化函數后,最后執行的結果,不是正相當于執行函數自己嗎?柯里化是不是把簡單的問題復雜化了?
沒錯,柯里化確實是把簡單的問題復雜化了,但在復雜化的同時,我們在使用函數時擁有了更多的自由度。對于函數參數的自由處理,正是柯里化的核心所在。
下面舉一個常見的例子。
如果想要驗證一串數字是否是正確的手機號,那么按照正常思路來做,可能就會寫出代碼如下唉:
fuction checkPhone(phoneNumber) {return /^1[34578]\d{9}$/.test(phoneNumber); }復制代碼而如果想要驗證是否是郵箱呢?你然后在寫一個,可是我們還會遇到更多需要驗證的消息,如“身份證、登錄名、密碼...”。為了偷懶,我們應該封裝一個更為通用的函數,把待驗證的正則表達式與將要被驗證的字符串作為參數傳入:
function check(reg, targetString) {return reg.test(targetSting); }復制代碼但是這樣封裝之后,在使用時又會遇到問題,因為總是需要輸入一串正則,一串字符,這樣就導致使用時效率低下。
這個時候,我們就可以借助柯里化,在check的基礎上再做一層封裝,以簡化使用。
var _check = createCurry(check);var checkPhone = _check(/xxxxxx/); var checkEmail = _check(/xxxxxx/);復制代碼最后在使用時就會變得更加簡潔與直觀了。
checkPhone('13979227922'); checkEmail('xsxsx@163.com');復制代碼在這個過程中可以發現,柯里化能夠應對更加復雜的邏輯封裝。當情況變得多變時,柯里化依然能夠應付自如。
雖然柯里化在一定程度上將問題復雜化,也讓代碼變得更加不容易理解,但是柯里化在面對復雜情況時的靈活性卻讓我們不得不愛。
總結
以上是生活随笔為你收集整理的深入理解javascript系列(十七):函数柯里化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 编程开发之--Oracle数据库--存储
- 下一篇: 0619-dedeCMS的安装、重装、目