<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
承接上一篇《XDM,JS如何函數語言程式設計?看這就夠了!(一)》,我們知道了函數語言程式設計的幾個基本概念。
這裡作簡要回顧:
本篇將著重介紹第 2 點中函數的輸入,它是 JS 輕量函數語言程式設計的基礎之基礎,重要之重要!!!
我們經常會寫出這樣的程式碼:
function ajax(url,data,callback) { // .. } function getPerson(data,cb) { ajax( "http://some.api/person", data, cb ); }
ajax 函數有三個入參,在 getPerson 函數裡呼叫,其中 url 已確定,data 和 cb 兩個引數則等待傳入。(因為很多時候引數都不是在當前能確定的,需要等待其它函數的操作後確定了再繼續傳入)
但是我們的原則是:入參最理想的情況下只需一個!
怎樣優化,可以實現這一點呢?
我們或許可以在外層再套一個函數來進一步確定傳參,比如:
function getCurrentUser(cb) { ...// 通過某些操作拿到 CURRENT_USER_ID getPerson( { user: CURRENT_USER_ID }, cb ); }
這樣,data 引數也已經確定,cb 引數仍等待傳入;函數 getCurrentUser 就只有一個入參了!
資料的傳遞路線是:
ajax(url,data,callback) => getPerson(data,cb) => getCurrentUser(cb)
這樣函數引數個數逐漸減少的過程就是偏應用。
也可以說:getCurrentUser(cb) 是 getOrder(data,cb) 的偏函數,getOrder(data,cb) 是 ajax(url,data,cb) 函數的偏函數。
設想下:
如果一個函數是這樣的:
function receiveMultiParam(a,b,c,......,x,y,z){ // .. }
我們難道還要像上面那樣手動指定外層函數進行逐層巢狀嗎?
顯示我們不會這麼做!
我們只需要封裝一個 partial(..) 函數:
function partial(fn,...presetArgs) { return function partiallyApplied(...laterArgs){ return fn( ...presetArgs, ...laterArgs ); }; }
它的基礎邏輯是:
var partial = (fn, ...presetArgs) => (...laterArgs) => fn( ...presetArgs, ...laterArgs );
把函數作為入參!還記得我們之前所說:
一個函數如果可以接受或返回一個甚至多個函數,它被叫做高階函數。
我們借用 partial() 來實現上述舉例:
var getPerson = partial( ajax, "http://some.api/person" ); var getCurrentUser = partial( getPerson, { user: CURRENT_USER_ID } ); // 版本 1
以下函數內部分析非常重要:
getPerson() 的內部執行機制是:
var getPerson = function partiallyApplied(...laterArgs) { return ajax( "http://some.api/person", ...laterArgs ); };
getCurrentUser() 的內部執行機制是:
var getCurrentUser = function outerPartiallyApplied(...outerLaterArgs) { var getPerson = function innerPartiallyApplied(...innerLaterArgs){ return ajax( "http://some.api/person", ...innerLaterArgs ); }; return getPerson( { user: CURRENT_USER_ID }, ...outerLaterArgs ); }
資料進行了傳遞:
getCurrentUser(outerLaterArgs) => getPerson(innerLaterArgs) => ajax(...params)
我們通過這樣一層額外的函數包裝層,實現了更加強大的資料傳遞,
我們將需要減少引數輸入的函數傳入 partial()中作為第一個引數,剩下的是 presetArgs,當前已知幾個,就可以寫幾個。還有不確定的入參 laterArgs,可以在確定後繼續追加。
像這樣進行額外的高階函數包裝層,是函數語言程式設計的精髓所在!
“隨著本系列的繼續深入,我們將會把許多函數互相包裝起來。記住,這就是函數語言程式設計!” —— 《JavaScript 輕量級函數語言程式設計》
實際上,實現 getCurrentUser() 還可以這樣寫:
// 版本 2 var getCurrentUser = partial( ajax, "http://some.api/person", { user: CURRENT_USER_ID } ); // 內部實現機制 var getCurrentUser = function partiallyApplied(...laterArgs) { return ajax( "http://some.api/person", { user: CURRENT_USER_ID }, ...laterArgs ); };
但是版本 1 因為重用了已經定義好的函數,所以它在表達上更清晰一些。它被認為更加貼合函數語言程式設計精神!
我們再看看 partial() 函數還可它用:
function partial(fn,...presetArgs) { return function partiallyApplied(...laterArgs){ return fn( ...presetArgs, ...laterArgs ); }; }
比如:將陣列 [1,2,3,4,5] 每項都加 3,通常我們會這麼做:
function add(x,y) { return x + y [1,2,3,4,5].map( function adder(val){ return add( 3, val ); } ); // [4,5,6,7,8]
藉助 partial():
[1,2,3,4,5].map( partial( add, 3 ) ); // [4,5,6,7,8]
add(..) 不能直接傳入 map(..) 函數裡,通過偏應用進行處理後則能傳入;
實際上,partial() 函數還可以有很多變體:
回想我們之前呼叫 Ajax 函數的方式:ajax( url, data, cb )。如果要偏應用 cb 而稍後再指定 data 和 url 引數,我們應該怎麼做呢?
function reverseArgs(fn) { return function argsReversed(...args){ return fn( ...args.reverse() ); }; } function partialRight( fn, ...presetArgs ) { return reverseArgs( partial( reverseArgs( fn ), ...presetArgs.reverse() ) ); } var cacheResult = partialRight( ajax, function onResult(obj){ cache[obj.id] = obj; }); // 處理後: cacheResult( "http://some.api/person", { user: CURRENT_USER_ID } );
函數柯里化實際上是一種特殊的偏函數。
我們用 curry(..) 函數來實現此前的 ajax(..) 例子,它會是這樣的:
var curriedAjax = curry( ajax ); var personFetcher = curriedAjax( "http://some.api/person" ); var getCurrentUser = personFetcher( { user: CURRENT_USER_ID } ); getCurrentUser( function foundUser(user){ /* .. */ } );
柯里化函數:接收單一實參(實參個數:1)並返回另一個接收下一個實參的函數。
它將一個函數從可呼叫的 f(a, b, c) 轉換為可呼叫的 f(a)(b)(c)。
實現:
function curry(fn,arity = fn.length) { return (function nextCurried(prevArgs){ return function curried(nextArg){ var args = prevArgs.concat( [nextArg] ); if (args.length >= arity) { return fn( ...args ); } else { return nextCurried( args ); } }; })( [] ); }
我們為什麼要如此著重去談“偏函數”(partial(sum,1,2)(3))或“柯里化”(sum(1)(2)(3))呢?
第一,是顯而易見的,偏函數或柯里化,可以將“指定分離實參”的時機和地方獨立開來;
第二,更有重要意義的是,當函數只有一個形參時,我們能夠比較容易地組合它們。這種單元函數,便於進行後續的組合函數;
對函數進行包裝,使其成為一個高階函數是函數語言程式設計的精髓!
至此,有了“偏函數”這門武器大炮,我們將逐漸轟開 JS輕量級函數語言程式設計的面紗 ~
以上就是JS輕量級函數語言程式設計實現XDM二的詳細內容,更多關於JS輕量級函數語言程式設計XDM的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45