摘要:函數(shù)柯里化在函數(shù)式編程中,函數(shù)是一等公民。函數(shù)柯里化的主要作用和特點(diǎn)就是參數(shù)復(fù)用提前返回和延遲執(zhí)行??赡茉趯?shí)際應(yīng)用場(chǎng)景中,很少使用函數(shù)柯里化的解決方案,但是了解認(rèn)識(shí)函數(shù)柯里化對(duì)自身的提升還是有幫助的。
最近在整理面試資源的時(shí)候,發(fā)現(xiàn)一道有意思的題目,所以就記錄下來。
題目如何實(shí)現(xiàn) multi(2)(3)(4)=24?
首先來分析下這道題,實(shí)現(xiàn)一個(gè) multi 函數(shù)并依次傳入?yún)?shù)執(zhí)行,得到最終的結(jié)果。通過題目很容易得到的結(jié)論是,把傳入的參數(shù)相乘就能夠得到需要的結(jié)果,也就是 2X3X4 = 24。
簡單的實(shí)現(xiàn)那么如何實(shí)現(xiàn) multi 函數(shù)去計(jì)算出結(jié)果值呢?腦海中首先浮現(xiàn)的解決方案是,閉包。
function multi(a) { return function(b) { return function(c) { return a * b * c; } } }
利用閉包的原則,multi 函數(shù)執(zhí)行的時(shí)候,返回 multi 函數(shù)中的內(nèi)部函數(shù),再次執(zhí)行的時(shí)候其實(shí)執(zhí)行的是這個(gè)內(nèi)部函數(shù),這個(gè)內(nèi)部函數(shù)中接著又嵌套了一個(gè)內(nèi)部函數(shù),用于計(jì)算最終結(jié)果并返回。
單純從題面來說,似乎是已經(jīng)實(shí)現(xiàn)了想要的結(jié)果,但仔細(xì)一想就會(huì)發(fā)現(xiàn)存在問題。
上面的實(shí)現(xiàn)方案存在的缺陷:
代碼不夠優(yōu)雅,實(shí)現(xiàn)步驟需要一層一層的嵌套函數(shù)。
可擴(kuò)展性差,假如是要實(shí)現(xiàn) multi(2)(3)(4)...(n) 這樣的功能,那就得嵌套 n 層函數(shù)。
那么有沒有更好的解決方案,答案是,使用函數(shù)式編程中的函數(shù)柯里化實(shí)現(xiàn)。
函數(shù)柯里化在函數(shù)式編程中,函數(shù)是一等公民。那么函數(shù)柯里化是怎樣的呢?
函數(shù)柯里化指的是將能夠接收多個(gè)參數(shù)的函數(shù)轉(zhuǎn)化為接收單一參數(shù)的函數(shù),并且返回接收余下參數(shù)且返回結(jié)果的新函數(shù)的技術(shù)。
函數(shù)柯里化的主要作用和特點(diǎn)就是參數(shù)復(fù)用、提前返回和延遲執(zhí)行。
例如:封裝兼容現(xiàn)代瀏覽器和 IE 瀏覽器的事件監(jiān)聽的方法,正常情況下封裝是這樣的。
var addEvent = function(el, type, fn, capture) { if(window.addEventListener) { el.addEventListener(type, function(e) { fn.call(el, e); }, capture); }else { el.attachEvent("on" + type, function(e) { fn.call(el, e); }) } }
該封裝的方法存在的不足是,每次寫監(jiān)聽事件的時(shí)候調(diào)用 addEvent 函數(shù),都會(huì)進(jìn)行 if else 的兼容性判斷。事實(shí)上在代碼中只需要執(zhí)行一次兼容性判斷就可以了,后續(xù)的事件監(jiān)聽就不需要再去判斷兼容性了。那么怎么用函數(shù)柯里化優(yōu)化這個(gè)封裝函數(shù)。
var addEvent = (function() { if(window.addEventListener) { return function(el, type, fn, capture) { el.addEventListener(type, function(e) { fn.call(el, e); }, capture); } }else { return function(ele, type, fn) { el.attachEvent("on" + type, function(e) { fn.call(el, e); }) } } })()
js 引擎在執(zhí)行該段代碼的時(shí)候就會(huì)進(jìn)行兼容性判斷,并且返回需要使用的事件監(jiān)聽封裝函數(shù)。這里使用了函數(shù)柯里化的兩個(gè)特點(diǎn):提前返回和延遲執(zhí)行。
柯里化另一個(gè)典型的應(yīng)用場(chǎng)景就是 bind 函數(shù)的實(shí)現(xiàn)。使用了函數(shù)柯里化的兩個(gè)特點(diǎn):參數(shù)復(fù)用和提前返回。
Function.prototype.bind = function(){ var fn = this; var args = Array.prototye.slice.call(arguments); var context = args.shift(); return function(){ return fn.apply(context, args.concat(Array.prototype.slice.call(arguments))); }; };柯里化的實(shí)現(xiàn)
那么如何通過函數(shù)柯里化實(shí)現(xiàn)面試題的功能呢?
通用版function curry(fn) { var args = Array.prototype.slice.call(arguments, 1); return function() { var newArgs = args.concat(Array.prototype.slice.call(arguments)); return fn.apply(this, newArgs); } }
curry 函數(shù)的第一個(gè)參數(shù)是要?jiǎng)討B(tài)創(chuàng)建柯里化的函數(shù),余下的參數(shù)存儲(chǔ)在 args 變量中。
執(zhí)行 curry 函數(shù)返回的函數(shù)接收新的參數(shù)與 args 變量存儲(chǔ)的參數(shù)合并,并把合并的參數(shù)傳入給柯里化了的函數(shù)。
function multiFn(a, b, c) { return a * b * c; } var multi = curry(multiFn); multi(2,3,4);
結(jié)果:
雖然得到的結(jié)果是一樣的,但是很容易發(fā)現(xiàn)存在問題,就是代碼相對(duì)于之前的閉包實(shí)現(xiàn)方式較復(fù)雜,而且執(zhí)行方式也不是題目要求的那樣 multi(2)(3)(4)。那么下面就來改進(jìn)這版代碼。
改進(jìn)版就題目而言,是需要執(zhí)行三次函數(shù)調(diào)用,那么針對(duì)柯里化后的函數(shù),如果傳入的參數(shù)沒有 3 個(gè)的話,就繼續(xù)執(zhí)行 curry 函數(shù)接收參數(shù),如果參數(shù)達(dá)到 3 個(gè),就執(zhí)行柯里化了的函數(shù)。
function curry(fn, args) { var length = fn.length; var args = args || []; return function(){ newArgs = args.concat(Array.prototype.slice.call(arguments)); if(newArgs.length < length){ return curry.call(this,fn,newArgs); }else{ return fn.apply(this,newArgs); } } } function multiFn(a, b, c) { return a * b * c; } var multi = curry(multiFn); multi(2)(3)(4); multi(2,3,4); multi(2)(3,4); multi(2,3)(4);
可以看到,通過改進(jìn)版的柯里化函數(shù),已經(jīng)將題目定的實(shí)現(xiàn)方式擴(kuò)展到好幾種了。這種實(shí)現(xiàn)方案的代碼擴(kuò)展性就比較強(qiáng)了,但是還是有點(diǎn)不足,就是必須事先知道求值的參數(shù)個(gè)數(shù),那能不能讓代碼更靈活點(diǎn),達(dá)到隨意傳參的效果,例如: multi(2)(3)(4),multi(5)(6)(7)(8)(9) 這樣的。
優(yōu)化版function multi() { var args = Array.prototype.slice.call(arguments); var fn = function() { var newArgs = args.concat(Array.prototype.slice.call(arguments)); return multi.apply(this, newArgs); } fn.toString = function() { return args.reduce(function(a, b) { return a * b; }) } return fn; }
這樣的解決方案就可以靈活的使用了。不足的是返回值是 Function 類型。
總結(jié)就題目本身而言,是存在多種實(shí)現(xiàn)方式的,只要理解并充分利用閉包的強(qiáng)大。
可能在實(shí)際應(yīng)用場(chǎng)景中,很少使用函數(shù)柯里化的解決方案,但是了解認(rèn)識(shí)函數(shù)柯里化對(duì)自身的提升還是有幫助的。
理解閉包和函數(shù)柯里化之后,如果在面試中遇到類似的題型,應(yīng)該就可以迎刃而解了。
后記本著學(xué)習(xí)和總結(jié)的態(tài)度寫的技術(shù)輸出,文中有任何錯(cuò)誤和問題,請(qǐng)大家指出。更多的技術(shù)輸出可以查看我的 github博客。
整理了一些前端的學(xué)習(xí)資源,希望能夠幫助到有需要的人,地址: 學(xué)習(xí)資源匯總。
參考https://segmentfault.com/q/10...
https://blog.csdn.net/crystal...
https://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651228431&idx=1&sn=c9d62a30a52f4572cc0cb4aaf2a82ef3
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/97071.html
摘要:忍者秘籍一書中,對(duì)于柯里化的定義如下在一個(gè)函數(shù)中首先填充幾個(gè)參數(shù)然后再返回一個(gè)新函數(shù)的技術(shù)稱為柯里化?;氐轿覀兊念}目本身,其實(shí)根據(jù)測(cè)試用例我們可以發(fā)現(xiàn),函數(shù)的要求就是接受單一函數(shù),例如但是與柯里化不同之處在于,柯里化返回的一個(gè)新函數(shù)。 歡迎大家再一次來到我的文章專欄:從面試題中我們能學(xué)到什么,各位同行小伙伴是否已經(jīng)開始了悠閑的春節(jié)假期呢?在這里提前祝大家雞年大吉吧~哈哈,之前有人說...
摘要:原題如下寫一個(gè)方法,當(dāng)使用下面的語法調(diào)用時(shí),能正常工作這道題要考察的,就是對(duì)函數(shù)柯里化的理解。當(dāng)參數(shù)只有一個(gè)的時(shí)候,進(jìn)行柯里化的處理。這其實(shí)就是函數(shù)柯里化的簡單應(yīng)用。 showImg(https://segmentfault.com/img/bVbopGm?w=620&h=350); 前言 這是前端面試題系列的第 6 篇,你可能錯(cuò)過了前面的篇章,可以在這里找到: ES6 中箭頭函數(shù)的...
摘要:函數(shù)柯里化關(guān)于函數(shù)柯里化的問題最初是在忍者秘籍中講閉包的部分中看到的,相信很多同學(xué)見過這樣一道和柯里化有關(guān)的面試題實(shí)現(xiàn)一個(gè)函數(shù),使得如下斷言能夠能夠通過簡單說就是實(shí)現(xiàn)一個(gè)求值函數(shù),能夠?qū)⑺袇?shù)相加得出結(jié)果。方法返回一個(gè)表示該對(duì)象的字符串。 函數(shù)柯里化 ??關(guān)于函數(shù)柯里化的問題最初是在《JavaScript忍者秘籍》中講閉包的部分中看到的,相信很多同學(xué)見過這樣一道和柯里化有關(guān)的面試題:...
摘要:題目發(fā)現(xiàn)一道有意思的面試題如何實(shí)現(xiàn)首先簡單分析一下,我們就能發(fā)現(xiàn)這是一個(gè)函數(shù)傳值次得到。簡單實(shí)現(xiàn)利用閉包,執(zhí)行函數(shù)時(shí)一個(gè)匿名函數(shù),用于最終返回結(jié)果。當(dāng)然,這個(gè)方法有個(gè)明顯缺陷,就是如果函數(shù)變成,我們就又要手動(dòng)嵌套一層。 題目 發(fā)現(xiàn)一道有意思的面試題:如何實(shí)現(xiàn) add(1)(2)(3)=6 ? 首先簡單分析一下,我們就能發(fā)現(xiàn)這是一個(gè)函數(shù)傳值 return3次得到6 。 簡單實(shí)現(xiàn) func...
閱讀 3409·2022-01-04 14:20
閱讀 3122·2021-09-22 15:08
閱讀 2211·2021-09-03 10:44
閱讀 2326·2019-08-30 15:44
閱讀 1503·2019-08-29 18:40
閱讀 2673·2019-08-29 17:09
閱讀 2998·2019-08-26 13:53
閱讀 3229·2019-08-26 13:37