摘要:至此,簡(jiǎn)化版的就完成了??梢钥闯?,的實(shí)現(xiàn)從頭到尾都是函數(shù)式編程的思想,下一篇文章打算結(jié)合社區(qū)的一道問(wèn)答題來(lái)介紹一下如何用函數(shù)式思想來(lái)解決問(wèn)題。我也是初學(xué)函數(shù)式,有什么說(shuō)的不準(zhǔn)確的地方希望多多指正。
前言
上一篇文章介紹了javascript中的compose函數(shù)的實(shí)現(xiàn),我是用了遞歸的思想去讓函數(shù)依次執(zhí)行,lodash中是用了迭代的思想依次執(zhí)行函數(shù),但實(shí)現(xiàn)了以后我還是覺(jué)得有些別扭,仔細(xì)想想,我們實(shí)現(xiàn)的是一個(gè)函數(shù)式編程用到的函數(shù),但是實(shí)現(xiàn)的方法還是太命令式了,函數(shù)還是命令式的執(zhí)行,通俗點(diǎn)說(shuō),還是太把函數(shù)當(dāng)成函數(shù)了,在我的理解中,函數(shù)和普通變量沒(méi)什么區(qū)別,只是執(zhí)行的方法不一樣,一旦賦予了函數(shù)這個(gè)執(zhí)行的屬性,我們就可以完全將函數(shù)當(dāng)成普通變量去對(duì)待。
實(shí)現(xiàn) 1.函數(shù)世界的加號(hào)函數(shù)和普通變量沒(méi)什么區(qū)別,只是需要偶爾執(zhí)行一下
舉個(gè)例子
1 + 2 = 3 "a" + "b" = "ab" func1 "+" func2 -> func3
前兩個(gè)例子就是普通變量的操作,最后一個(gè)例子是函數(shù)的操作,本質(zhì)上看來(lái),沒(méi)有任何區(qū)別,兩個(gè)函數(shù)作用的結(jié)果就是生成一個(gè)函數(shù),只不過(guò)在函數(shù)的世界里,這個(gè)加號(hào)的意義就是如何變換生成一個(gè)新的函數(shù),回到compose來(lái),在compose中,加號(hào)的意義就是把一個(gè)函數(shù)的執(zhí)行結(jié)果當(dāng)成下一個(gè)函數(shù)的輸入,最后在生成一個(gè)函數(shù),就像下面這樣
var fn = (func1, func2) => (...args) => func2.call(this, func1.apply(this, args))
在這個(gè)例子里面,func1的執(zhí)行結(jié)果就是func2的參數(shù),并且生成了一個(gè)新的函數(shù)fn,我們給這個(gè)fn傳遞參數(shù),它就會(huì)作為func1的參數(shù)來(lái)啟動(dòng)執(zhí)行,最后得到了函數(shù)依次執(zhí)行的效果,這就是最簡(jiǎn)單的compose,這個(gè)函數(shù)就是ramda.js實(shí)現(xiàn)compsoe需要的第一個(gè)函數(shù)_pipe
var _pipe = (f, g) => (...args) => g.call(this, f.apply(this, args))
_pipe就定義了compose中所謂加號(hào)的意義了。
2."不一樣的"reduce在這里提到了reduce,是不是有一點(diǎn)感覺(jué),reduce的作用就是讓一個(gè)數(shù)組不斷的執(zhí)行下去,所以肯定能和咱們這個(gè)compose有點(diǎn)聯(lián)系,先舉個(gè)reduce最常用的例子,求數(shù)組的和
var a = [1,2,3,4,5] a.reduce((x, y) => x + y, 0)
這個(gè)就是不斷的將兩個(gè)數(shù)求和,生成一個(gè)新的數(shù),再去和下一個(gè)數(shù)求和,最后得到15,下面想一下,如果把數(shù)字換成函數(shù)會(huì)怎么樣,兩個(gè)函數(shù)結(jié)合生成一個(gè)新的函數(shù),這個(gè)結(jié)合法則就使用上面的_pipe,這個(gè)新的函數(shù)再去結(jié)合下一個(gè)函數(shù),直到最后一個(gè)函數(shù)執(zhí)行完,我們得到的還是函數(shù),我們前面說(shuō)了,函數(shù)知識(shí)偶爾需要執(zhí)行一下,這個(gè)函數(shù)的生成和執(zhí)行過(guò)程是反向遞歸的過(guò)程。利用這個(gè)思想,就可以寥寥幾行(甚至只需要一行)就寫(xiě)出來(lái)這個(gè)非常函數(shù)式的compose了
var reverse = arr => arr.reverse() var _pipe = (f, g) => (...args) => g.call(this, f.apply(this, args)); var compose = (...args) => reverse(args).reduce(_pipe, args.shift())
舉個(gè)例子驗(yàn)證一下,我們把首個(gè)函數(shù)做多元處理,再upperCase,再repeat
var classyGreeting = (firstName, lastName) => "The name"s " + lastName + ", " + firstName + " " + lastName var toUpper = str => str.toUpperCase() var repeat = str => str.repeat(2) var result = compose(repeat, toUpper, classyGreeting)("dong", "zhe") // THE NAME"S ZHE, DONG ZHETHE NAME"S ZHE, DONG ZHE
我在這里把函數(shù)生成過(guò)程分析一下
首先我們用_pipe組合classyGreeting,toUpper
f1 = _pipe(classyGreeting, toUpper) f1 = (...args) => toUpper.call(this, classyGreeting.apply(this, args))
_pipe繼續(xù)結(jié)合f1, repeat
f2 = _pipe(f1, repeat) f2 = (...args) => repeat.call(this, f1.apply(this, args))
函數(shù)的執(zhí)行過(guò)程就會(huì)將參數(shù)層層傳遞到最里面的classyGreeting開(kāi)始執(zhí)行,從而完成函數(shù)的依次執(zhí)行。ramda.js自己實(shí)現(xiàn)了reduce,不僅支持?jǐn)?shù)組的reduce,還支持多種數(shù)據(jù)結(jié)構(gòu)的reduce,(兼容性也更好?),下一步來(lái)分析是如何自己實(shí)現(xiàn)數(shù)組的reduce的,可與看出,自己分離出來(lái)邏輯之后,函數(shù)的執(zhí)行過(guò)程和組合的規(guī)則部分將分離的更徹底。
3.自己寫(xiě)一個(gè)reducereduce接受三個(gè)參數(shù),執(zhí)行函數(shù),初始值,執(zhí)行隊(duì)列(可以不止為一個(gè)數(shù)組),返回一個(gè)針對(duì)這些參數(shù)的reduce處理,這里只寫(xiě)數(shù)組部分(_arrayReduce),源碼中還包含了關(guān)于迭代器的_iterableReduce 等等,而且ramda.js對(duì)執(zhí)行函數(shù)也有一層對(duì)象封裝,擴(kuò)展了函數(shù)的功能
var reduce = (fn, acc, list) => (fn = _xwrap(fn), _arrayReduce(fn, acc, list))
在寫(xiě)_arrayReduce之前,先來(lái)看一下函數(shù)的對(duì)象封裝_xwrap
var _xwrap = (function(){ function XWrap(fn) { this.f = fn; } XWrap.prototype["@@transducer/init"] = function() { throw new Error("init not implemented on XWrap"); }; XWrap.prototype["@@transducer/result"] = function(acc) { return acc; }; XWrap.prototype["@@transducer/step"] = function(acc, x) { return this.f(acc, x); }; return function _xwrap(fn) { return new XWrap(fn); }; })()
其實(shí)就是對(duì)函數(shù)執(zhí)行狀態(tài)做了一個(gè)分類管理
@@transducer/step 這種狀態(tài)認(rèn)為是一種過(guò)程狀態(tài)
@@transducer/result 這種狀態(tài)被認(rèn)為是一種結(jié)果狀態(tài)
這種狀態(tài)管理通過(guò)對(duì)象也是合情合理的
最后再來(lái)完成_arrayReduce,就很簡(jiǎn)單了,這個(gè)函數(shù)只是專心一件事情,就是寫(xiě)reduce的過(guò)程規(guī)則。
var _arrayReduce = (xf, acc, list) => { var idx = 0 var len = list.length while (idx < len) { acc = xf["@@transducer/step"](acc, list[idx]); idx += 1; } return xf["@@transducer/result"](acc); }
至此,ramda.js簡(jiǎn)化版的reduce就完成了。
4.其他一些功能tail用來(lái)分離初始值和執(zhí)行隊(duì)列的,因?yàn)槌跏己瘮?shù)是多元的(接收多個(gè)參數(shù)),執(zhí)行隊(duì)列都是一元(接收一個(gè)參數(shù))的,分離還是有必要的
var tail = arr => arr.slice(1)
reverse改變執(zhí)行順序
var reverse = arr => arr.reverse()
_arity我把源代碼貼出來(lái),我也不知道為什么這樣做,可能是明確指定參數(shù)吧,因?yàn)?b>reduce生成的函數(shù)是可以接受多個(gè)參數(shù)的,_arity就是處理這個(gè)函數(shù)的
var _arity = (n, fn) => { switch (n) { case 0: return function() { return fn.apply(this, arguments); }; case 1: return function(a0) { return fn.apply(this, arguments); }; case 2: return function(a0, a1) { return fn.apply(this, arguments); }; case 3: return function(a0, a1, a2) { return fn.apply(this, arguments); }; case 4: return function(a0, a1, a2, a3) { return fn.apply(this, arguments); }; case 5: return function(a0, a1, a2, a3, a4) { return fn.apply(this, arguments); }; case 6: return function(a0, a1, a2, a3, a4, a5) { return fn.apply(this, arguments); }; case 7: return function(a0, a1, a2, a3, a4, a5, a6) { return fn.apply(this, arguments); }; case 8: return function(a0, a1, a2, a3, a4, a5, a6, a7) { return fn.apply(this, arguments); }; case 9: return function(a0, a1, a2, a3, a4, a5, a6, a7, a8) { return fn.apply(this, arguments); }; case 10: return function(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) { return fn.apply(this, arguments); }; default: throw new Error("First argument to _arity must be a non-negative integer no greater than ten"); } }5.整合
最后整合出來(lái)兩個(gè)最終的函數(shù)pipe和compose
var pipe = (...args) => _arity(args[0].length, reduce(_pipe, args[0], tail(args))) var remdaCompose = (...args) => pipe.apply(this, reverse(args))
再把上面的demo試一下
console.log(remdaCompose(repeat, toUpper, classyGreeting)("dong", "zhe")) // THE NAME"S ZHE, DONG ZHETHE NAME"S ZHE, DONG ZHE
整合的完全版我放到了github里
總結(jié)這篇文章主要分析了ramda.js實(shí)現(xiàn)compose的過(guò)程,其中分析了如何把函數(shù)看成一等公民,如何實(shí)現(xiàn)一個(gè)reduce等等??梢钥闯?,compose的實(shí)現(xiàn)從頭到尾都是函數(shù)式編程的思想,下一篇文章打算結(jié)合社區(qū)的一道問(wèn)答題來(lái)介紹一下如何用函數(shù)式思想來(lái)解決問(wèn)題。我也是初學(xué)函數(shù)式,有什么說(shuō)的不準(zhǔn)確的地方希望多多指正。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/81755.html
摘要:專題系列第十六篇,講解函數(shù)組合,并且使用柯里化和函數(shù)組合實(shí)現(xiàn)模式需求我們需要寫(xiě)一個(gè)函數(shù),輸入,返回。這便是函數(shù)組合。 JavaScript 專題系列第十六篇,講解函數(shù)組合,并且使用柯里化和函數(shù)組合實(shí)現(xiàn) pointfree 模式 需求 我們需要寫(xiě)一個(gè)函數(shù),輸入 kevin,返回 HELLO, KEVIN。 嘗試 var toUpperCase = function(x) { return...
摘要:結(jié)論這次主要介紹了函數(shù)式編程中的函數(shù)的原理和實(shí)現(xiàn)方法,由于篇幅原因,我把打算分析的源碼實(shí)現(xiàn)放到下一篇來(lái)介紹,可以說(shuō)實(shí)現(xiàn)的更加函數(shù)式,需要單獨(dú)好好分析。 上一篇文章介紹了javascript函數(shù)式編程中curry(柯里化)的實(shí)現(xiàn),當(dāng)然那個(gè)柯里化是有限參數(shù)的柯里化,等有機(jī)會(huì)在補(bǔ)上無(wú)限參數(shù)的那一種柯里化,這次主要說(shuō)的是javascript函數(shù)式編程中另外一個(gè)很重要的函數(shù)compose,com...
摘要:前言這是源碼分析系列文章的第三篇,前面兩篇文章源碼分析一源碼分析二分別分析了中的一些重要函數(shù),也給出了簡(jiǎn)化的實(shí)現(xiàn),為理解其內(nèi)部機(jī)理和執(zhí)行方式提供了便利。官方也對(duì)其進(jìn)行了說(shuō)明。 前言 這是Lodash源碼分析系列文章的第三篇,前面兩篇文章(Lodash 源碼分析(一)Function Methods、Lodash 源碼分析(二)Function Methods)分別分析了Lodash F...
摘要:源碼解析模塊的代碼十分簡(jiǎn)練,但是實(shí)現(xiàn)的作用卻是十分強(qiáng)大。只傳遞一個(gè)參數(shù)的時(shí)候,就直接把這個(gè)函數(shù)返回返回組合函數(shù)這就是對(duì)源碼的一個(gè)整體解讀,水平有限,歡迎拍磚。后續(xù)的源碼解讀和測(cè)試?yán)涌梢躁P(guān)注源碼解讀倉(cāng)庫(kù) compose源碼解析 compose模塊的代碼十分簡(jiǎn)練,但是實(shí)現(xiàn)的作用卻是十分強(qiáng)大。redux為何稱為redux?有人說(shuō)就是reduce和flux的結(jié)合體,而reduce正是comp...
摘要:用法回顧執(zhí)行順序每當(dāng)執(zhí)行時(shí),執(zhí)行下一個(gè)中間件,執(zhí)行到最后一個(gè)中間件后開(kāi)始往回執(zhí)行源碼解析源碼執(zhí)行步驟使用方法即將進(jìn)中的數(shù)組中方法調(diào)用的和方法來(lái)創(chuàng)建服務(wù),的回掉執(zhí)行下面的操作回掉首先執(zhí)行方法將組合成一個(gè)對(duì)象來(lái)執(zhí)行,這個(gè)對(duì)象即可完成中 用法回顧 const Koa = require(koa); const app = new Koa(); app.use(async (ctx, nex...
閱讀 3831·2021-11-24 10:46
閱讀 1790·2021-11-15 11:38
閱讀 3853·2021-11-15 11:37
閱讀 3683·2021-10-27 14:19
閱讀 2042·2021-09-03 10:36
閱讀 2065·2021-08-16 11:02
閱讀 3065·2019-08-30 15:55
閱讀 2328·2019-08-30 15:44