以前的系列,咱們介紹了什麼是高階函數。全部以函數做爲參數的函數,均可以叫做高階函數。而且咱們經常利用高階函數來封裝一些公共邏輯。正則表達式
本次,咱們要繼續學習,繼續記錄,柯里化。柯里化,其實就是高階函數的一種特殊用法。數組
柯里化是指這樣一個函數(假設叫作createCurry),它接收函數A做爲參數,運行後可以返回一個新的函數,而且這個新的函數可以處理函數A的剩餘參數。bash
文字老是不那麼好去理解,下面咱們就經過例子來理解吧。閉包
假設有一個接收三個參數的函數A。app
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的全部剩餘參數。所以柯里化也被稱爲部分求值。ui
在簡單的場景下,咱們能夠不借助柯里化通用式來轉化獲得柯里化函數,僅憑藉眼力本身封裝。spa
例如,有一個簡單的加法函數,它可以將自身的三個參數加起來並返回計算結果。code
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.length
var 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);
}
//參數收集完畢,執行func
return 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');複製代碼
在這個過程當中能夠發現,柯里化可以應對更加複雜的邏輯封裝。當狀況變得多變時,柯里化依然可以應付自如。
雖然柯里化在必定程度上將問題複雜化,也讓代碼變得更加不容易理解,可是柯里化在面對複雜狀況時的靈活性卻讓咱們不得不愛。