摘要:函數(shù)式接口之前在上看到一個(gè)技術(shù)視頻,講的接口為什么不好用,以及什么樣的接口更好用。演講者是的作者,他提出了一種全面函數(shù)式的接口設(shè)計(jì)模式。言歸正傳,今天聊聊這樣的接口如何實(shí)現(xiàn),以及中的模塊。
函數(shù)式 js 接口
之前在 youtube 上看到一個(gè)技術(shù)視頻,講“underscore.js的接口為什么不好用”,以及什么樣的接口更好用。演講者是 lodash.js 的作者,他提出了一種“全面函數(shù)式”的 js 接口設(shè)計(jì)模式。大概類似這樣:
// 傳統(tǒng)接口 _.map([1, 2, 3], function (el) {return el * 2}); // return [2, 4, 6] // 函數(shù)式接口 var fn = _.map([1, 2, 3]); // return a function fn(function (el) {return el * 2}); // return [2, 4, 6]; // 或者 _.map([1, 2, 3])(function (el) {return el * 2}); // return [2, 4, 6];
找到一點(diǎn)感覺(jué)沒(méi)有?其實(shí)就是函數(shù)式編程語(yǔ)言中廣泛存在的“科里化”函數(shù)。當(dāng)實(shí)參填滿形參表的時(shí)候,執(zhí)行結(jié)算返回結(jié)果,否則返回一個(gè)臨時(shí)函數(shù),繼續(xù)接受實(shí)參。
看到這個(gè)寫法眼前一亮,感覺(jué)有大規(guī)模簡(jiǎn)化代碼的潛力。當(dāng)時(shí)實(shí)際試了一下發(fā)下很多地方用不了,因?yàn)橹皩懙拇a受 jQuery 影響,有很多這樣的接口:
foobar.attribute(name); // 讀屬性 foobar.attribute(name, newValue); // 寫屬性
這樣的接口是按照上述方法 curry 化會(huì)使得讀屬性變得不可能,根本原因是參數(shù)數(shù)量不同時(shí) attribute 函數(shù)的語(yǔ)義根本不一樣。使用 jQuery 的時(shí)候感覺(jué)這種寫法非常爽,后來(lái)就跟著這么寫,但是目前看來(lái)這樣的接口設(shè)計(jì)是有問(wèn)題的。
言歸正傳,今天聊聊這樣的接口如何實(shí)現(xiàn),以及 lodash 中的 fp 模塊。
實(shí)現(xiàn)原理說(shuō)到底就是個(gè) currying 的問(wèn)題,currying 在很多語(yǔ)言中是內(nèi)置功能,但是 js 沒(méi)有,所以我們要實(shí)現(xiàn)一個(gè) currying 工具函數(shù)。首先貼一個(gè)最簡(jiǎn)易的 currying 實(shí)現(xiàn),它的功能非常簡(jiǎn)單,輸入一個(gè)函數(shù) fn1 和部分實(shí)參,返回一個(gè)保存部分實(shí)參,繼續(xù)接收實(shí)參的函數(shù) fn2,調(diào)用fn2,它會(huì)合并實(shí)參數(shù)組,并調(diào)用 fn1。
/** * 函數(shù)柯里化 * @param fn 輸入函數(shù) * @return 柯里化后的函數(shù) */ var curry = function (fn) { if (!isFunction(fn)) { return; } var args = slice(arguments, 1); return function () { return fn.apply(this, args.concat(slice(arguments, 0))); } }
isFunction 和 slice 大家都知道我就不貼了。看一下如何調(diào)用:
function add(a, b) { return a + b; } addOne = curry(add, 1); addOne(2); // return 3
有時(shí)候我們需要輸入的部分實(shí)參是數(shù)組列表形式,所以我們包裝一下剛才的 curry 函數(shù):
/** * 函數(shù)柯里化 * @param fn 輸入函數(shù) * @param arr 參數(shù)列表 * @return 柯里化后的函數(shù) */ var curryApply = function (fn, arr) { if (!isFunction(fn)) { return; } var args = arr.slice(0); args.unshift(fn); return curry.apply(this, args); }
上面的 curry 函數(shù)有個(gè)問(wèn)題,就是連續(xù)多次補(bǔ)充實(shí)參,我們還需要封裝一個(gè)支持連續(xù)調(diào)用的版本:
/** * 自動(dòng)柯里化 * @param fn 輸入函數(shù) * @param n 輸入函數(shù)參數(shù)個(gè)數(shù) * @return 柯里化后的函數(shù) */ var autoCurry = function (fn, n) { if (!isFunction(fn)) { return; } function retFn() { var len = arguments.length; var args = slice(arguments, 0); var nextn = n - len; if (nextn > 0) { return autoCurry(curryApply(retFn, args), nextn); } return fn.apply(this, args); } return retFn; }
autoCurry 使用的遞歸的方法,輸出函數(shù)可以可以通過(guò)簡(jiǎn)單調(diào)用的方式連續(xù)補(bǔ)充實(shí)參,當(dāng)實(shí)參和預(yù)設(shè)的參數(shù)數(shù)量相等時(shí),執(zhí)行輸入函數(shù)。使用方法如下:
function compute(a, b, c) { return (a + b) * c; } var curryedCompute = autoCurry(compute, 3); compute(1, 2, 3); // return 9 curryedCompute(1)(2)(3); // return 9
大家如果使用 node.js 的話,可能知道 npm 中有個(gè) curry 模塊,實(shí)現(xiàn)的功能是一樣的,不同的是當(dāng)你不輸入?yún)?shù)個(gè)數(shù) n 時(shí),curry 模塊 會(huì)使用 Function 對(duì)象的 length 屬性作為預(yù)設(shè)的 n 值。
lodash/fp到這里實(shí)現(xiàn)原理就講清楚了。本著不造輪子的原則,如果大家想嘗試一下函數(shù)式風(fēng)格的基礎(chǔ) js 庫(kù)的話,建議使用 lodash/fp 這個(gè)模塊。大家都知道 lodash 是 underscore 的 better implemention,而 lodash/fp 就是科里化的 lodash。與簡(jiǎn)單的 currying 不同的是,為了方便使用,lodash/fp 的設(shè)計(jì)者調(diào)換了一些接口的參數(shù)順序,比如開頭提到的 _.map 接口,如果簡(jiǎn)單 currying 的話第一個(gè)參數(shù)應(yīng)該是數(shù)組[1, 2, 3],但是大多數(shù)時(shí)候,我們想要持有的是一個(gè)算法,用這個(gè)算法處理不同的數(shù)據(jù)。所以我們希望暫存的實(shí)際上是第二個(gè)參數(shù) fn,所以 lodash/fp 的接口是這樣的:
// The `lodash/map` iteratee receives three arguments: // (value, index|key, collection) _.map(["6", "8", "10"], parseInt); // → [6, NaN, 2] // The `lodash/fp/map` iteratee is capped at one argument: // (value) fp.map(parseInt)(["6", "8", "10"]); // → [6, 8, 10]
關(guān)于 lodash/fp 更詳細(xì)的說(shuō)明,請(qǐng)看:https://github.com/lodash/lodash/wiki/FP-Guide
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/79724.html
摘要:為了盡可能提升互通性,已經(jīng)成為函數(shù)式編程庫(kù)遵循的實(shí)際標(biāo)準(zhǔn)。與輕量級(jí)函數(shù)式編程的概念相反,它以火力全開的姿態(tài)進(jìn)軍的函數(shù)式編程世界。 原文地址:Functional-Light-JS 原文作者:Kyle Simpson-《You-Dont-Know-JS》作者 關(guān)于譯者:這是一個(gè)流淌著滬江血液的純粹工程:認(rèn)真,是 HTML 最堅(jiān)實(shí)的梁柱;分享,是 CSS 里最閃耀的一瞥;總結(jié),...
摘要:本書主要探索函數(shù)式編程的核心思想。我們?cè)谥袘?yīng)用的僅僅是一套基本的函數(shù)式編程概念的子集。我稱之為輕量級(jí)函數(shù)式編程。通常來(lái)說(shuō),關(guān)于函數(shù)式編程的書籍都熱衷于拓展閱讀者的知識(shí)面,并企圖覆蓋更多的知識(shí)點(diǎn)。,本書統(tǒng)稱為函數(shù)式編程者。 原文地址:Functional-Light-JS 原文作者:Kyle Simpson - 《You-Dont-Know-JS》作者 譯者團(tuán)隊(duì)(排名不分先后)...
摘要:上傳圖片本地預(yù)覽功能靜態(tài)方法會(huì)創(chuàng)建一個(gè),其中包含一個(gè)表示參數(shù)中給出的對(duì)象的。這個(gè)的生命周期和創(chuàng)建它的窗口中的綁定。這個(gè)新的對(duì)象表示指定的對(duì)象或?qū)ο蟆J菍?duì)數(shù)組中每一項(xiàng)運(yùn)行給定函數(shù),如果該函數(shù)對(duì)任一項(xiàng)返回,則返回。 13、meta標(biāo)簽的用法 http://www.alenqi.site/2018/03/04/complete-tags/ 14、隨機(jī)生...
摘要:函數(shù)通常是面向?qū)ο缶幊田L(fēng)格,具有副作用。因?yàn)樵诤瘮?shù)式編程中,很有可能這些引用指向的并不是同一個(gè)對(duì)象。記住,函數(shù)并不意味著函數(shù)式編程。函數(shù)可以用函數(shù)式編程風(fēng)格編寫,避免副作用并不修改參數(shù),但這并不保證。 軟件構(gòu)建系列 原文鏈接:Functional Mixins 譯者注:在編程中,mixin 類似于一個(gè)固有名詞,可以理解為混合或混入,通常不進(jìn)行直譯,本文也是同樣。 這是軟件構(gòu)建系列教...
摘要:前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn)分為新聞熱點(diǎn)開發(fā)教程工程實(shí)踐深度閱讀開源項(xiàng)目巔峰人生等欄目。背后的故事本文是對(duì)于年之間世界發(fā)生的大事件的詳細(xì)介紹,闡述了從提出到角力到流產(chǎn)的前世今生。 前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn);分為新聞熱點(diǎn)、開發(fā)教程、工程實(shí)踐、深度閱讀、開源項(xiàng)目、巔峰人生等欄目。歡迎...
閱讀 2374·2021-11-22 14:56
閱讀 1181·2019-08-30 15:55
閱讀 3213·2019-08-29 13:29
閱讀 1362·2019-08-26 13:56
閱讀 3504·2019-08-26 13:37
閱讀 567·2019-08-26 13:33
閱讀 3354·2019-08-26 13:33
閱讀 2235·2019-08-26 13:33